| // 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 "chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.h" |
| |
| #include <cmath> |
| #include <memory> |
| #include <string> |
| |
| #include "base/allocator/buildflags.h" |
| #include "base/allocator/partition_allocator/partition_alloc_features.h" |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/cpu.h" |
| #include "base/feature_list.h" |
| #include "base/macros.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/sparse_histogram.h" |
| #include "base/rand_util.h" |
| #include "base/system/sys_info.h" |
| #include "base/task/task_traits.h" |
| #include "base/task/thread_pool.h" |
| #include "base/threading/scoped_blocking_call.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "build/config/compiler/compiler_buildflags.h" |
| #include "chrome/browser/about_flags.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/buildflags.h" |
| #include "chrome/browser/chrome_browser_main.h" |
| #include "chrome/browser/google/google_brand.h" |
| #include "chrome/browser/metrics/bluetooth_available_utility.h" |
| #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" |
| #include "chrome/browser/metrics/power/battery_level_provider.h" |
| #include "chrome/browser/metrics/power/power_metrics_reporter.h" |
| #include "chrome/browser/metrics/process_memory_metrics_emitter.h" |
| #include "chrome/browser/metrics/usage_scenario/usage_scenario_tracker.h" |
| #include "chrome/browser/shell_integration.h" |
| #include "components/flags_ui/pref_service_flags_storage.h" |
| #include "components/policy/core/common/management/management_service.h" |
| #include "components/policy/core/common/management/platform_management_service.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/common/content_switches.h" |
| #include "services/resource_coordinator/public/cpp/memory_instrumentation/browser_metrics.h" |
| #include "ui/base/pointer/pointer_device.h" |
| #include "ui/base/ui_base_switches.h" |
| #include "ui/display/screen.h" |
| |
| #if !defined(OS_ANDROID) |
| #include "chrome/browser/metrics/first_web_contents_profiler.h" |
| #include "chrome/browser/metrics/tab_stats/tab_stats_tracker.h" |
| #endif // !defined(OS_ANDROID) |
| |
| #if defined(OS_ANDROID) && defined(__arm__) |
| #include <cpu-features.h> |
| #endif // defined(OS_ANDROID) && defined(__arm__) |
| |
| // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch |
| // of lacros-chrome is complete. |
| #if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) |
| #include <gnu/libc-version.h> |
| |
| #include "base/linux_util.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/version.h" |
| #if defined(USE_X11) |
| #include "ui/base/ui_base_features.h" |
| #include "ui/base/x/x11_util.h" |
| #endif |
| #endif // defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) |
| |
| #if defined(USE_OZONE) || defined(USE_X11) |
| #include "ui/events/devices/device_data_manager.h" |
| #include "ui/events/devices/input_device_event_observer.h" |
| #endif // defined(USE_OZONE) || defined(USE_X11) |
| |
| #if defined(OS_WIN) |
| #include "base/win/base_win_buildflags.h" |
| #include "base/win/scoped_handle.h" |
| #include "base/win/windows_version.h" |
| #include "chrome/browser/shell_integration_win.h" |
| #endif // defined(OS_WIN) |
| |
| #if BUILDFLAG(IS_CHROMEOS_LACROS) |
| #include "chromeos/crosapi/cpp/crosapi_constants.h" |
| #endif // BUILDFLAG(IS_CHROMEOS_LACROS) |
| |
| namespace { |
| |
| void RecordMemoryMetrics(); |
| |
| // Records memory metrics after a delay. |
| void RecordMemoryMetricsAfterDelay() { |
| content::GetUIThreadTaskRunner({})->PostDelayedTask( |
| FROM_HERE, base::BindOnce(&RecordMemoryMetrics), |
| memory_instrumentation::GetDelayForNextMemoryLog()); |
| } |
| |
| // Records memory metrics, and then triggers memory colleciton after a delay. |
| void RecordMemoryMetrics() { |
| scoped_refptr<ProcessMemoryMetricsEmitter> emitter( |
| new ProcessMemoryMetricsEmitter); |
| emitter->FetchAndEmitProcessMemoryMetrics(); |
| |
| RecordMemoryMetricsAfterDelay(); |
| } |
| |
| // These values are written to logs. New enum values can be added, but existing |
| // enums must never be renumbered or deleted and reused. |
| enum UMALinuxDistro { |
| UMA_LINUX_DISTRO_UNKNOWN = 0, |
| UMA_LINUX_DISTRO_ARCH = 1, |
| UMA_LINUX_DISTRO_CENTOS = 2, |
| UMA_LINUX_DISTRO_DEBIAN = 3, |
| UMA_LINUX_DISTRO_ELEMENTARY = 4, |
| UMA_LINUX_DISTRO_FEDORA = 5, |
| UMA_LINUX_DISTRO_MINT = 6, |
| UMA_LINUX_DISTRO_OPENSUSE_LEAP = 7, |
| UMA_LINUX_DISTRO_RHEL = 8, |
| UMA_LINUX_DISTRO_SUSE_ENTERPRISE = 9, |
| UMA_LINUX_DISTRO_UBUNTU = 10, |
| |
| // Note: Add new distros to the list above this line, and update Linux.Distro2 |
| // in tools/metrics/histograms/enums.xml accordingly. |
| UMA_LINUX_DISTRO_MAX |
| }; |
| |
| enum UMALinuxGlibcVersion { |
| UMA_LINUX_GLIBC_NOT_PARSEABLE, |
| UMA_LINUX_GLIBC_UNKNOWN, |
| UMA_LINUX_GLIBC_2_11, |
| // To log newer versions, just update tools/metrics/histograms/histograms.xml. |
| }; |
| |
| enum UMATouchEventFeatureDetectionState { |
| UMA_TOUCH_EVENT_FEATURE_DETECTION_ENABLED, |
| UMA_TOUCH_EVENT_FEATURE_DETECTION_AUTO_ENABLED, |
| UMA_TOUCH_EVENT_FEATURE_DETECTION_AUTO_DISABLED, |
| UMA_TOUCH_EVENT_FEATURE_DETECTION_DISABLED, |
| // NOTE: Add states only immediately above this line. Make sure to |
| // update the enum list in tools/metrics/histograms/histograms.xml |
| // accordingly. |
| UMA_TOUCH_EVENT_FEATURE_DETECTION_STATE_COUNT |
| }; |
| |
| #if BUILDFLAG(IS_CHROMEOS_LACROS) |
| // These values are written to logs. New enum values can be added, but existing |
| // enums must never be renumbered or deleted and reused. |
| enum class ChromeOSChannel { |
| kUnknown = 0, |
| kCanary = 1, |
| kDev = 2, |
| kBeta = 3, |
| kStable = 4, |
| kMaxValue = kStable, |
| }; |
| |
| // Records the underlying Chrome OS release channel, which may be different than |
| // the Lacros browser's release channel. |
| void RecordChromeOSChannel() { |
| ChromeOSChannel os_channel = ChromeOSChannel::kUnknown; |
| std::string release_track; |
| if (base::SysInfo::GetLsbReleaseValue(crosapi::kChromeOSReleaseTrack, |
| &release_track)) { |
| if (release_track == crosapi::kReleaseChannelStable) |
| os_channel = ChromeOSChannel::kStable; |
| else if (release_track == crosapi::kReleaseChannelBeta) |
| os_channel = ChromeOSChannel::kBeta; |
| else if (release_track == crosapi::kReleaseChannelDev) |
| os_channel = ChromeOSChannel::kDev; |
| else if (release_track == crosapi::kReleaseChannelCanary) |
| os_channel = ChromeOSChannel::kCanary; |
| } |
| base::UmaHistogramEnumeration("ChromeOS.Lacros.OSChannel", os_channel); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS_LACROS) |
| |
| void RecordMicroArchitectureStats() { |
| #if defined(ARCH_CPU_X86_FAMILY) |
| base::CPU cpu; |
| base::CPU::IntelMicroArchitecture arch = cpu.GetIntelMicroArchitecture(); |
| base::UmaHistogramEnumeration("Platform.IntelMaxMicroArchitecture", arch, |
| base::CPU::MAX_INTEL_MICRO_ARCHITECTURE); |
| #endif // defined(ARCH_CPU_X86_FAMILY) |
| base::UmaHistogramSparse("Platform.LogicalCpuCount", |
| base::SysInfo::NumberOfProcessors()); |
| } |
| |
| #if defined(OS_WIN) |
| bool IsApplockerRunning(); |
| #endif // defined(OS_WIN) |
| |
| // Called on a background thread, with low priority to avoid slowing down |
| // startup with metrics that aren't trivial to compute. |
| void RecordStartupMetrics() { |
| #if defined(OS_WIN) |
| const base::win::OSInfo& os_info = *base::win::OSInfo::GetInstance(); |
| int patch = os_info.version_number().patch; |
| int build = os_info.version_number().build; |
| int patch_level = 0; |
| |
| if (patch < 65536 && build < 65536) |
| patch_level = MAKELONG(patch, build); |
| DCHECK(patch_level) << "Windows version too high!"; |
| base::UmaHistogramSparse("Windows.PatchLevel", patch_level); |
| |
| int kernel32_patch = os_info.Kernel32VersionNumber().patch; |
| int kernel32_build = os_info.Kernel32VersionNumber().build; |
| int kernel32_patch_level = 0; |
| if (kernel32_patch < 65536 && kernel32_build < 65536) |
| kernel32_patch_level = MAKELONG(kernel32_patch, kernel32_build); |
| DCHECK(kernel32_patch_level) << "Windows kernel32.dll version too high!"; |
| base::UmaHistogramSparse("Windows.PatchLevelKernel32", kernel32_patch_level); |
| |
| base::UmaHistogramBoolean("Windows.HasHighResolutionTimeTicks", |
| base::TimeTicks::IsHighResolution()); |
| |
| // Determine if Applocker is enabled and running. This does not check if |
| // Applocker rules are being enforced. |
| base::UmaHistogramBoolean("Windows.ApplockerRunning", IsApplockerRunning()); |
| #endif // defined(OS_WIN) |
| |
| bluetooth_utility::ReportBluetoothAvailability(); |
| |
| // Record whether Chrome is the default browser or not. |
| shell_integration::DefaultWebClientState default_state = |
| shell_integration::GetDefaultBrowser(); |
| base::UmaHistogramEnumeration("DefaultBrowser.State", default_state, |
| shell_integration::NUM_DEFAULT_STATES); |
| |
| #if BUILDFLAG(IS_CHROMEOS_LACROS) |
| RecordChromeOSChannel(); |
| #endif |
| } |
| |
| // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch |
| // of lacros-chrome is complete. |
| #if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) |
| void RecordLinuxDistroSpecific(const std::string& version_string, |
| size_t parts, |
| const char* histogram_name) { |
| base::Version version{version_string}; |
| if (!version.IsValid() || version.components().size() < parts) |
| return; |
| |
| base::CheckedNumeric<int32_t> sample = 0; |
| for (size_t i = 0; i < parts; i++) { |
| sample *= 1000; |
| sample += version.components()[i]; |
| } |
| |
| if (sample.IsValid()) |
| base::UmaHistogramSparse(histogram_name, sample.ValueOrDie()); |
| } |
| |
| void RecordLinuxDistro() { |
| UMALinuxDistro distro_result = UMA_LINUX_DISTRO_UNKNOWN; |
| |
| std::vector<std::string> distro_tokens = |
| base::SplitString(base::GetLinuxDistro(), base::kWhitespaceASCII, |
| base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| if (distro_tokens.size() > 0) { |
| if (distro_tokens[0] == "Ubuntu") { |
| // Format: Ubuntu YY.MM.P [LTS] |
| // We are only concerned with release (YY.MM) not the patch (P). |
| distro_result = UMA_LINUX_DISTRO_UBUNTU; |
| if (distro_tokens.size() >= 2) |
| RecordLinuxDistroSpecific(distro_tokens[1], 2, "Linux.Distro.Ubuntu"); |
| } else if (distro_tokens[0] == "openSUSE") { |
| // Format: openSUSE Leap RR.R |
| distro_result = UMA_LINUX_DISTRO_OPENSUSE_LEAP; |
| if (distro_tokens.size() >= 3 && distro_tokens[1] == "Leap") { |
| RecordLinuxDistroSpecific(distro_tokens[2], 2, |
| "Linux.Distro.OpenSuseLeap"); |
| } |
| } else if (distro_tokens[0] == "Debian") { |
| // Format: Debian GNU/Linux R.P (<codename>) |
| // We are only concerned with the release (R) not the patch (P). |
| distro_result = UMA_LINUX_DISTRO_DEBIAN; |
| if (distro_tokens.size() >= 3) |
| RecordLinuxDistroSpecific(distro_tokens[2], 1, "Linux.Distro.Debian"); |
| } else if (distro_tokens[0] == "Fedora") { |
| // Format: Fedora RR (<codename>) |
| distro_result = UMA_LINUX_DISTRO_FEDORA; |
| if (distro_tokens.size() >= 2) |
| RecordLinuxDistroSpecific(distro_tokens[1], 1, "Linux.Distro.Fedora"); |
| } else if (distro_tokens[0] == "Arch") { |
| // Format: Arch Linux |
| distro_result = UMA_LINUX_DISTRO_ARCH; |
| } else if (distro_tokens[0] == "CentOS") { |
| // Format: CentOS [Linux] <version> (<codename>) |
| distro_result = UMA_LINUX_DISTRO_CENTOS; |
| } else if (distro_tokens[0] == "elementary") { |
| // Format: elementary OS <release name> |
| distro_result = UMA_LINUX_DISTRO_ELEMENTARY; |
| } else if (distro_tokens.size() >= 2 && distro_tokens[1] == "Mint") { |
| // Format: Linux Mint RR |
| distro_result = UMA_LINUX_DISTRO_MINT; |
| if (distro_tokens.size() >= 3) |
| RecordLinuxDistroSpecific(distro_tokens[2], 1, "Linux.Distro.Mint"); |
| } else if (distro_tokens.size() >= 4 && distro_tokens[0] == "Red" && |
| distro_tokens[1] == "Hat" && distro_tokens[2] == "Enterprise" && |
| distro_tokens[3] == "Linux") { |
| // Format: Red Hat Enterprise Linux <variant> R.P (<codename>) |
| distro_result = UMA_LINUX_DISTRO_RHEL; |
| } else if (distro_tokens.size() >= 3 && distro_tokens[0] == "SUSE" && |
| distro_tokens[1] == "Linux" && |
| distro_tokens[2] == "Enterprise") { |
| // Format: SUSE Linux Enterprise <variant> RR |
| distro_result = UMA_LINUX_DISTRO_SUSE_ENTERPRISE; |
| } |
| } |
| |
| base::UmaHistogramSparse("Linux.Distro2", distro_result); |
| } |
| #endif // defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) |
| |
| void RecordLinuxGlibcVersion() { |
| // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch |
| // of lacros-chrome is complete. |
| #if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) |
| base::Version version(gnu_get_libc_version()); |
| |
| UMALinuxGlibcVersion glibc_version_result = UMA_LINUX_GLIBC_NOT_PARSEABLE; |
| if (version.IsValid() && version.components().size() == 2) { |
| glibc_version_result = UMA_LINUX_GLIBC_UNKNOWN; |
| uint32_t glibc_major_version = version.components()[0]; |
| uint32_t glibc_minor_version = version.components()[1]; |
| if (glibc_major_version == 2) { |
| // A constant to translate glibc 2.x minor versions to their |
| // equivalent UMALinuxGlibcVersion values. |
| const int kGlibcMinorVersionTranslationOffset = 11 - UMA_LINUX_GLIBC_2_11; |
| uint32_t translated_glibc_minor_version = |
| glibc_minor_version - kGlibcMinorVersionTranslationOffset; |
| if (translated_glibc_minor_version >= UMA_LINUX_GLIBC_2_11) { |
| glibc_version_result = |
| static_cast<UMALinuxGlibcVersion>(translated_glibc_minor_version); |
| } |
| } |
| } |
| base::UmaHistogramSparse("Linux.GlibcVersion", glibc_version_result); |
| #endif |
| } |
| |
| void RecordTouchEventState() { |
| const base::CommandLine& command_line = |
| *base::CommandLine::ForCurrentProcess(); |
| const std::string touch_enabled_switch = |
| command_line.HasSwitch(switches::kTouchEventFeatureDetection) |
| ? command_line.GetSwitchValueASCII( |
| switches::kTouchEventFeatureDetection) |
| : switches::kTouchEventFeatureDetectionAuto; |
| |
| UMATouchEventFeatureDetectionState state; |
| if (touch_enabled_switch.empty() || |
| touch_enabled_switch == switches::kTouchEventFeatureDetectionEnabled) { |
| state = UMA_TOUCH_EVENT_FEATURE_DETECTION_ENABLED; |
| } else if (touch_enabled_switch == |
| switches::kTouchEventFeatureDetectionAuto) { |
| state = (ui::GetTouchScreensAvailability() == |
| ui::TouchScreensAvailability::ENABLED) |
| ? UMA_TOUCH_EVENT_FEATURE_DETECTION_AUTO_ENABLED |
| : UMA_TOUCH_EVENT_FEATURE_DETECTION_AUTO_DISABLED; |
| } else if (touch_enabled_switch == |
| switches::kTouchEventFeatureDetectionDisabled) { |
| state = UMA_TOUCH_EVENT_FEATURE_DETECTION_DISABLED; |
| } else { |
| NOTREACHED(); |
| return; |
| } |
| |
| base::UmaHistogramEnumeration("Touchscreen.TouchEventsEnabled", state, |
| UMA_TOUCH_EVENT_FEATURE_DETECTION_STATE_COUNT); |
| } |
| |
| #if defined(USE_OZONE) || defined(USE_X11) |
| |
| // Asynchronously records the touch event state when the ui::DeviceDataManager |
| // completes a device scan. |
| class AsynchronousTouchEventStateRecorder |
| : public ui::InputDeviceEventObserver { |
| public: |
| AsynchronousTouchEventStateRecorder(); |
| ~AsynchronousTouchEventStateRecorder() override; |
| |
| // ui::InputDeviceEventObserver overrides. |
| void OnDeviceListsComplete() override; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(AsynchronousTouchEventStateRecorder); |
| }; |
| |
| AsynchronousTouchEventStateRecorder::AsynchronousTouchEventStateRecorder() { |
| ui::DeviceDataManager::GetInstance()->AddObserver(this); |
| } |
| |
| AsynchronousTouchEventStateRecorder::~AsynchronousTouchEventStateRecorder() { |
| ui::DeviceDataManager::GetInstance()->RemoveObserver(this); |
| } |
| |
| void AsynchronousTouchEventStateRecorder::OnDeviceListsComplete() { |
| ui::DeviceDataManager::GetInstance()->RemoveObserver(this); |
| RecordTouchEventState(); |
| } |
| |
| #endif // defined(USE_OZONE) || defined(USE_X11) |
| |
| #if defined(OS_WIN) |
| void RecordPinnedToTaskbarProcessError(bool error) { |
| base::UmaHistogramBoolean("Windows.IsPinnedToTaskbar.ProcessError", error); |
| } |
| |
| void OnShellHandlerConnectionError() { |
| RecordPinnedToTaskbarProcessError(true); |
| } |
| |
| // Record the UMA histogram when a response is received. |
| void OnIsPinnedToTaskbarResult(bool succeeded, |
| bool is_pinned_to_taskbar, |
| bool is_pinned_to_taskbar_verb_check) { |
| RecordPinnedToTaskbarProcessError(false); |
| |
| // Used for histograms; do not reorder. |
| enum Result { NOT_PINNED = 0, PINNED = 1, FAILURE = 2, NUM_RESULTS }; |
| |
| Result result_no_verb_check = FAILURE; |
| Result result_verb_check = FAILURE; |
| if (succeeded) { |
| result_no_verb_check = is_pinned_to_taskbar ? PINNED : NOT_PINNED; |
| result_verb_check = is_pinned_to_taskbar_verb_check ? PINNED : NOT_PINNED; |
| } |
| |
| base::UmaHistogramEnumeration("Windows.IsPinnedToTaskbar", result_verb_check, |
| NUM_RESULTS); |
| base::UmaHistogramEnumeration("Windows.IsPinnedToTaskbar2", |
| result_no_verb_check, NUM_RESULTS); |
| } |
| |
| // Records the pinned state of the current executable into a histogram. Should |
| // be called on a background thread, with low priority, to avoid slowing down |
| // startup. |
| void RecordIsPinnedToTaskbarHistogram() { |
| shell_integration::win::GetIsPinnedToTaskbarState( |
| base::BindOnce(&OnShellHandlerConnectionError), |
| base::BindOnce(&OnIsPinnedToTaskbarResult)); |
| } |
| |
| class ScHandleTraits { |
| public: |
| typedef SC_HANDLE Handle; |
| |
| ScHandleTraits() = delete; |
| ScHandleTraits(const ScHandleTraits&) = delete; |
| ScHandleTraits& operator=(const ScHandleTraits&) = delete; |
| |
| // Closes the handle. |
| static bool CloseHandle(SC_HANDLE handle) { |
| return ::CloseServiceHandle(handle) != FALSE; |
| } |
| |
| // Returns true if the handle value is valid. |
| static bool IsHandleValid(SC_HANDLE handle) { return handle != nullptr; } |
| |
| // Returns null handle value. |
| static SC_HANDLE NullHandle() { return nullptr; } |
| }; |
| |
| typedef base::win::GenericScopedHandle<ScHandleTraits, |
| base::win::DummyVerifierTraits> |
| ScopedScHandle; |
| |
| bool IsApplockerRunning() { |
| ScopedScHandle scm_handle( |
| ::OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT)); |
| if (!scm_handle.IsValid()) |
| return false; |
| |
| ScopedScHandle service_handle( |
| ::OpenServiceW(scm_handle.Get(), L"appid", SERVICE_QUERY_STATUS)); |
| if (!service_handle.IsValid()) |
| return false; |
| |
| SERVICE_STATUS status; |
| if (!::QueryServiceStatus(service_handle.Get(), &status)) |
| return false; |
| |
| return status.dwCurrentState == SERVICE_RUNNING; |
| } |
| |
| #endif // defined(OS_WIN) |
| |
| #if !defined(OS_ANDROID) |
| // Returns whether the instance has an enterprise brand code. |
| bool HasEnterpriseBrandCode() { |
| std::string brand; |
| google_brand::GetBrand(&brand); |
| return google_brand::IsEnterprise(brand); |
| } |
| |
| // Returns whether the instance is domain joined. This doesn't include CBCM |
| // (EnterpriseManagementAuthority::DOMAIN_LOCAL). |
| bool IsDomainJoined() { |
| auto enterprise_management_authorities = |
| policy::PlatformManagementService::GetInstance() |
| .GetManagementAuthorities(); |
| return enterprise_management_authorities.contains( |
| policy::EnterpriseManagementAuthority::DOMAIN_LOCAL); |
| } |
| #endif // !defined(OS_ANDROID) |
| |
| void RecordDisplayHDRStatus(const display::Display& display) { |
| base::UmaHistogramBoolean("Hardware.Display.SupportsHDR", |
| display.color_spaces().SupportsHDR()); |
| } |
| |
| } // namespace |
| |
| ChromeBrowserMainExtraPartsMetrics::ChromeBrowserMainExtraPartsMetrics() |
| : display_count_(0) {} |
| |
| ChromeBrowserMainExtraPartsMetrics::~ChromeBrowserMainExtraPartsMetrics() = |
| default; |
| |
| void ChromeBrowserMainExtraPartsMetrics::PreProfileInit() { |
| RecordMicroArchitectureStats(); |
| } |
| |
| void ChromeBrowserMainExtraPartsMetrics::PreBrowserStart() { |
| flags_ui::PrefServiceFlagsStorage flags_storage( |
| g_browser_process->local_state()); |
| about_flags::RecordUMAStatistics(&flags_storage, "Launch.FlagsAtStartup"); |
| |
| // Log once here at browser start rather than at each renderer launch. |
| ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial("ClangPGO", |
| #if BUILDFLAG(CLANG_PGO) |
| #if BUILDFLAG(USE_THIN_LTO) |
| "EnabledWithThinLTO" |
| #else |
| "Enabled" |
| #endif |
| #else |
| "Disabled" |
| #endif |
| ); |
| |
| // Records whether or not the Segment heap is in use. |
| #if defined(OS_WIN) |
| |
| if (base::win::GetVersion() >= base::win::Version::WIN10_20H1) { |
| ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial("WinSegmentHeap", |
| #if BUILDFLAG(ENABLE_SEGMENT_HEAP) |
| "OptedIn" |
| #else |
| "OptedOut" |
| #endif |
| ); |
| } else { |
| ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial("WinSegmentHeap", |
| "NotSupported"); |
| } |
| |
| // Records whether or not CFG indirect call dispatch guards are present |
| // or not. |
| ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial("WinCFG", |
| #if BUILDFLAG(WIN_ENABLE_CFG_GUARDS) |
| "Enabled" |
| #else |
| "Disabled" |
| #endif |
| ); |
| |
| #endif // defined(OS_WIN) |
| |
| // Records whether or not PartitionAlloc is used as the default allocator. |
| ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial( |
| "PartitionAllocEverywhere", |
| #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) |
| "Enabled" |
| #else |
| "Disabled" |
| #endif |
| ); |
| |
| // Records whether or not PartitionAlloc-Everywhere is enabled, and whether |
| // PCScan is enabled on top of it. This is meant for a 3-way experiment with 2 |
| // binaries: |
| // - binary A: deployed to 33% users, with PA-E and PCScan off. |
| // - binary B: deployed to 66% users, with PA-E on, half of which having |
| // PCScan on |
| // |
| // NOTE, deliberately don't use PA_ALLOW_PCSCAN which depends on bitness. |
| // In the 32-bit case, PCScan is always disabled, but we'll deliberately |
| // misrepresent it as enabled here (and later ignored when analyzing results), |
| // in order to keep each population at 33%. |
| ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial( |
| "PartitionAllocEverywhereAndPCScan", |
| #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) |
| base::FeatureList::IsEnabled( |
| base::features::kPartitionAllocPCScanBrowserOnly) |
| ? "EnabledWithPCScan" |
| : "EnabledWithoutPCScan" |
| #else |
| "Disabled" |
| #endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) |
| ); |
| |
| #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) |
| // Records whether or not BackupRefPtr and/or PCScan is enabled. This is meant |
| // for a 3-way experiment with 2 binaries: |
| // - binary A: deployed to 66% users, with half of them having PCScan on and |
| // half off (BackupRefPtr fully off) |
| // - binary B: deployed to 33% users, with BackupRefPtr on (PCSCan fully off) |
| // |
| // NOTE, deliberately don't use PA_ALLOW_PCSCAN which depends on bitness. |
| // In the 32-bit case, PCScan is always disabled, but we'll deliberately |
| // misrepresent it as enabled here (and later ignored when analyzing results), |
| // in order to keep each population at 33%. |
| // |
| // Alsto note that USE_BACKUP_REF_PTR_FAKE is only used to fake that the |
| // feature is enabled for the purpose of this Finch setting, while in fact |
| // there are no behavior changes. |
| ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial( |
| "BackupRefPtrAndPCScan", |
| #if BUILDFLAG(USE_BACKUP_REF_PTR) || BUILDFLAG(USE_BACKUP_REF_PTR_FAKE) |
| "BackupRefPtrEnabled" |
| #else |
| base::FeatureList::IsEnabled( |
| base::features::kPartitionAllocPCScanBrowserOnly) |
| ? "PCScanEnabled" |
| : "Disabled" |
| #endif // BUILDFLAG(USE_BACKUP_REF_PTR) || BUILDFLAG(USE_BACKUP_REF_PTR_FAKE) |
| ); |
| #endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) |
| |
| #if defined(OS_ANDROID) |
| // No need to filter out on Android, because it doesn't support |
| // ChromeVariations policy. |
| constexpr bool is_enterprise = false; |
| #else |
| // Check for enterprises the same way that Google Update can check, to match |
| // with the experiment population (see the comment below). |
| // NOTE, this isn't perfect and won't catch all enterprises. |
| const bool is_enterprise = HasEnterpriseBrandCode() || IsDomainJoined(); |
| #endif |
| |
| // TODO(bartekn): Remove once the enterprise inclusion is verified. This is |
| // just meant to ensure that the enterprise portion of the |
| // BackupRefPtrNoEnterprise setting below does what's expected. |
| ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial( |
| "EnterpriseSynthetic", |
| is_enterprise ? "IsEnterprise" : "IsNotEnterprise"); |
| |
| // This synthetic field trial for the BackupRefPtr binary A/B experiment is |
| // set up such that: |
| // 1) Enterprises are excluded from experiment, to make sure we honor |
| // ChromeVariations policy. |
| // 2) The experiment binary (USE_BACKUP_REF_PTR) is delivered via Google |
| // Update to fraction X of the non-enterprise population. |
| // Note, USE_BACKUP_REF_PTR_FAKE is only used to fake that the feature is |
| // enabled for the purpose of this Finch setting, while in fact there are |
| // no behavior changes. Note, however, PCScan will be kept away from the |
| // fake experiment binary, just as it would be from a regular one. |
| // 3) The control group is established in fraction X of non-enterprise |
| // popluation via Finch (PartitionAllocBackupRefPtrControl). Since this |
| // Finch is applicable only to 1-X of the non-enterprise population, we |
| // need to set it to Y=X/(1-X). E.g. if X=.333, Y=.5; if X=.01, Y=.0101. |
| #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) |
| #if BUILDFLAG(USE_BACKUP_REF_PTR) || BUILDFLAG(USE_BACKUP_REF_PTR_FAKE) |
| constexpr bool kIsBrpOn = true; // experiment binary only |
| #else |
| constexpr bool kIsBrpOn = false; // non-experiment binary |
| #endif |
| const bool is_brp_control = base::FeatureList::IsEnabled( |
| base::features::kPartitionAllocBackupRefPtrControl); |
| const char* group_name; |
| if (is_enterprise) { |
| if (kIsBrpOn) { // is_enterprise && kIsBrpOn |
| group_name = "Excluded_Enterprise_BrpOn"; |
| } else { // is_enterprise && !kIsBrpOn |
| group_name = "Excluded_Enterprise_BrpOff"; |
| } |
| } else { |
| if (kIsBrpOn) { // !is_enterprise && kIsBrpOn |
| group_name = "Enabled"; |
| } else { // !is_enterprise && !kIsBrpOn |
| if (is_brp_control) { |
| group_name = "Control"; |
| } else { |
| group_name = "Excluded_NonEnterprise"; |
| } |
| } |
| } |
| ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial( |
| "BackupRefPtrNoEnterprise", group_name); |
| #endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) |
| } |
| |
| void ChromeBrowserMainExtraPartsMetrics::PostBrowserStart() { |
| RecordMemoryMetricsAfterDelay(); |
| RecordLinuxGlibcVersion(); |
| #if defined(USE_X11) |
| if (!features::IsUsingOzonePlatform()) { |
| // Ozone writes this histogram upon platform initialisation. |
| base::UmaHistogramEnumeration("Linux.WindowManager", |
| ui::GetWindowManagerUMA()); |
| } |
| #endif |
| |
| constexpr base::TaskTraits kBestEffortTaskTraits = { |
| base::MayBlock(), base::TaskPriority::BEST_EFFORT, |
| base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}; |
| // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch |
| // of lacros-chrome is complete. |
| #if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) |
| base::ThreadPool::PostTask(FROM_HERE, kBestEffortTaskTraits, |
| base::BindOnce(&RecordLinuxDistro)); |
| #endif |
| |
| #if defined(USE_OZONE) || defined(USE_X11) |
| // The touch event state for X11 and Ozone based event sub-systems are based |
| // on device scans that happen asynchronously. So we may need to attach an |
| // observer to wait until these scans complete. |
| if (ui::DeviceDataManager::GetInstance()->AreDeviceListsComplete()) { |
| RecordTouchEventState(); |
| } else { |
| input_device_event_observer_ = |
| std::make_unique<AsynchronousTouchEventStateRecorder>(); |
| } |
| #else |
| RecordTouchEventState(); |
| #endif // defined(USE_OZONE) || defined(USE_X11) |
| |
| #if defined(OS_MAC) |
| RecordMacMetrics(); |
| #endif // defined(OS_MAC) |
| |
| #if defined(OS_WIN) |
| // RecordStartupMetrics calls into shell_integration::GetDefaultBrowser(), |
| // which requires a COM thread on Windows. |
| base::ThreadPool::CreateCOMSTATaskRunner(kBestEffortTaskTraits) |
| ->PostTask(FROM_HERE, base::BindOnce(&RecordStartupMetrics)); |
| #else |
| base::ThreadPool::PostTask(FROM_HERE, kBestEffortTaskTraits, |
| base::BindOnce(&RecordStartupMetrics)); |
| #endif // defined(OS_WIN) |
| |
| #if defined(OS_WIN) |
| // TODO(isherman): The delay below is currently needed to avoid (flakily) |
| // breaking some tests, including all of the ProcessMemoryMetricsEmitterTest |
| // tests. Figure out why there is a dependency and fix the tests. |
| auto background_task_runner = |
| base::ThreadPool::CreateSequencedTaskRunner(kBestEffortTaskTraits); |
| |
| // The PinnedToTaskbar histogram is CPU intensive and can trigger a crashing |
| // bug in Windows or in shell extensions so just sample the data to reduce the |
| // cost. |
| if (base::RandGenerator(100) == 0) { |
| background_task_runner->PostDelayedTask( |
| FROM_HERE, base::BindOnce(&RecordIsPinnedToTaskbarHistogram), |
| base::TimeDelta::FromSeconds(45)); |
| } |
| #endif // defined(OS_WIN) |
| |
| auto* screen = display::Screen::GetScreen(); |
| display_count_ = screen->GetNumDisplays(); |
| base::UmaHistogramCounts100("Hardware.Display.Count.OnStartup", |
| display_count_); |
| |
| for (const auto& display : screen->GetAllDisplays()) { |
| RecordDisplayHDRStatus(display); |
| } |
| |
| display_observer_.emplace(this); |
| |
| #if !defined(OS_ANDROID) |
| metrics::BeginFirstWebContentsProfiling(); |
| // Only instantiate the tab stats tracker if a local state exists. This is |
| // always the case for Chrome but not for the unittests. |
| if (g_browser_process != nullptr && |
| g_browser_process->local_state() != nullptr) { |
| metrics::TabStatsTracker::SetInstance( |
| std::make_unique<metrics::TabStatsTracker>( |
| g_browser_process->local_state())); |
| } |
| #endif // !defined(OS_ANDROID) |
| #if defined(OS_MAC) || defined(OS_WIN) |
| // BatteryLevelProvider is supported on mac and windows only, thus we report |
| // power metrics only on those platforms. |
| if (performance_monitor::ProcessMonitor::Get()) { |
| // PowerMetricsReporter needs ProcessMonitor to be created. |
| usage_scenario_tracker_ = std::make_unique<UsageScenarioTracker>(); |
| power_metrics_reporter_ = std::make_unique<PowerMetricsReporter>( |
| usage_scenario_tracker_->data_store(), BatteryLevelProvider::Create()); |
| } |
| #endif // defined(OS_MAC) || defined (OS_WIN) |
| } |
| |
| void ChromeBrowserMainExtraPartsMetrics::PreMainMessageLoopRun() { |
| if (base::TimeTicks::IsConsistentAcrossProcesses()) { |
| // Enable I/O jank monitoring for the browser process. |
| base::EnableIOJankMonitoringForProcess(base::BindRepeating( |
| [](int janky_intervals_per_minute, int total_janks_per_minute) { |
| base::UmaHistogramCounts100( |
| "Browser.Responsiveness.IOJankyIntervalsPerMinute", |
| janky_intervals_per_minute); |
| base::UmaHistogramCounts1000( |
| "Browser.Responsiveness.IOJanksTotalPerMinute", |
| total_janks_per_minute); |
| })); |
| } |
| } |
| |
| void ChromeBrowserMainExtraPartsMetrics::OnDisplayAdded( |
| const display::Display& new_display) { |
| EmitDisplaysChangedMetric(); |
| RecordDisplayHDRStatus(new_display); |
| } |
| |
| void ChromeBrowserMainExtraPartsMetrics::OnDisplayRemoved( |
| const display::Display& old_display) { |
| EmitDisplaysChangedMetric(); |
| } |
| |
| void ChromeBrowserMainExtraPartsMetrics::OnDisplayMetricsChanged( |
| const display::Display& display, |
| uint32_t changed_metrics) { |
| if (changed_metrics & DisplayObserver::DISPLAY_METRIC_COLOR_SPACE) { |
| RecordDisplayHDRStatus(display); |
| } |
| } |
| |
| void ChromeBrowserMainExtraPartsMetrics::EmitDisplaysChangedMetric() { |
| int display_count = display::Screen::GetScreen()->GetNumDisplays(); |
| if (display_count != display_count_) { |
| display_count_ = display_count; |
| base::UmaHistogramCounts100("Hardware.Display.Count.OnChange", |
| display_count_); |
| } |
| } |
| |
| namespace chrome { |
| |
| void AddMetricsExtraParts(ChromeBrowserMainParts* main_parts) { |
| main_parts->AddParts(std::make_unique<ChromeBrowserMainExtraPartsMetrics>()); |
| } |
| |
| } // namespace chrome |