blob: 36fe36dc725cc5983cacdfcff22bf755c4cdad72 [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// 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 <algorithm>
#include <cmath>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include "base/allocator/partition_alloc_support.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/cpu.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/sparse_histogram.h"
#include "base/power_monitor/power_monitor_buildflags.h"
#include "base/rand_util.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.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 "base/trace_event/trace_log.h"
#include "base/version.h"
#include "build/build_config.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/enterprise/browser_management/management_service_factory.h"
#include "chrome/browser/google/google_brand.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/metrics/process_memory_metrics_emitter.h"
#include "chrome/browser/metrics/tab_stats/tab_stats_tracker.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/browser/ui/performance_controls/performance_controls_metrics.h"
#include "chrome/browser/web_applications/sampling_metrics_provider.h"
#include "chrome/common/chrome_switches.h"
#include "components/metrics/android_metrics_helper.h"
#include "components/performance_manager/public/performance_manager.h"
#include "components/policy/core/common/management/management_service.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/variations/variations_switches.h"
#include "components/webui/flags/pref_service_flags_storage.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_switches.h"
#include "crypto/unexportable_key.h"
#include "crypto/unexportable_key_metrics.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 !BUILDFLAG(IS_ANDROID)
#include "base/power_monitor/battery_state_sampler.h"
#include "chrome/browser/metrics/first_web_contents_profiler.h"
#include "chrome/browser/metrics/power/battery_discharge_reporter.h"
#include "chrome/browser/metrics/power/power_metrics_reporter.h"
#include "chrome/browser/metrics/power/process_monitor.h"
#endif // !BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_ANDROID)
#include "base/android/apk_info.h"
#if defined(__arm__)
#include <cpu-features.h>
#endif
#endif // BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_LINUX)
#if defined(__GLIBC__)
#include <gnu/libc-version.h>
#endif // defined(__GLIBC__)
#include "base/linux_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#endif // BUILDFLAG(IS_LINUX)
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/win/hardware_check.h"
#include "base/win/registry.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "chrome/browser/metrics/key_credential_manager_support_reporter_win.h"
#include "chrome/browser/shell_integration_win.h"
#include "chrome/browser/win/cloud_synced_folder_checker.h"
#include "chrome/installer/util/taskbar_util.h"
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_LINUX)
#include "chrome/browser/metrics/pressure/pressure_metrics_reporter.h"
#endif // BUILDFLAG(IS_LINUX)
#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
#include "components/user_manager/user_manager.h"
#endif // BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "components/power_metrics/system_power_monitor.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "base/mac/process_requirement.h"
#include "chrome/common/chrome_version.h"
#endif // BUILDFLAG(IS_MAC)
namespace {
// The number of restarts to wait until removing the enable-benchmarking flag.
constexpr int kEnableBenchmarkingCountdownDefault = 3;
constexpr char kEnableBenchmarkingPrefId[] = "enable_benchmarking_countdown";
#if BUILDFLAG(IS_MAC)
constexpr char kUnexportableKeysKeychainAccessGroup[] =
MAC_TEAM_IDENTIFIER_STRING "." MAC_BUNDLE_IDENTIFIER_STRING
".unexportable-keys";
#endif // BUILDFLAG(IS_MAC)
void RecordMemoryMetrics();
// Gets the delay for logging memory related metrics. Minimum is 1 second.
base::TimeDelta GetDelayForNextMemoryLogTest() {
int test_delay_in_minutes;
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kTestMemoryLogDelayInMinutes) &&
base::StringToInt(command_line->GetSwitchValueASCII(
switches::kTestMemoryLogDelayInMinutes),
&test_delay_in_minutes)) {
// Setting --test-memory-log-delay-in-minutes=0 is useful for testing the
// feature, but zero delay tends to overwhelm the system.
return test_delay_in_minutes <= 0 ? base::Seconds(1)
: base::Minutes(test_delay_in_minutes);
}
return memory_instrumentation::GetDelayForNextMemoryLog();
}
// Records memory metrics after a delay.
void RecordMemoryMetricsAfterDelay() {
content::GetUIThreadTaskRunner({})->PostDelayedTask(
FROM_HERE, base::BindOnce(&RecordMemoryMetrics),
GetDelayForNextMemoryLogTest());
}
// Records memory metrics, and then triggers memory collection after a delay.
void RecordMemoryMetrics() {
scoped_refptr<ProcessMemoryMetricsEmitter> emitter(
new ProcessMemoryMetricsEmitter);
emitter->FetchAndEmitProcessMemoryMetrics();
performance_manager::PerformanceManager::RecordMemoryMetrics();
RecordMemoryMetricsAfterDelay();
}
// These values are written to logs. New enum values can be added, but existing
// enums must never be renumbered or deleted and reused unless the histogram is
// renamed.
enum class UmaLinuxDistro {
kUnknown = 0,
kOther = 1,
kAlma = 2,
kAlpine = 3,
kAlter = 4,
kAmazon = 5,
kAnarchy = 6,
kAntergos = 7,
kAntiX = 8,
kAoscOs = 9,
kAperio = 10,
kApricity = 11,
kArch = 12,
kArcoLinux = 13,
kArtix = 14,
kArya = 15,
kAsteroidOs = 16,
kBedrock = 17,
kBitrig = 18,
kBlackArch = 19,
kBlag = 20,
kBlankOn = 21,
kBlueLight = 22,
kBodhi = 23,
kBonsai = 24,
kBunsenLabs = 25,
kCalculate = 26,
kCarbs = 27,
kCblMariner = 28,
kCelOs = 29,
kCentOs = 30,
kChakra = 31,
kChaletOs = 32,
kChapeau = 33,
kCleanjaro = 34,
kClearLinux = 35,
kClearOs = 36,
kClover = 37,
kCondres = 38,
kContainerLinux = 39,
kCrux = 40,
kCrystalLinux = 41,
kCucumber = 42,
kCyberOs = 43,
kDahlia = 44,
kDarkOs = 45,
kDebian = 46,
kDeepin = 47,
kDesaOs = 48,
kDevuan = 49,
kDracOs = 50,
kDrauger = 51,
kElementary = 52,
kEndeavourOs = 53,
kEndless = 54,
kEuroLinux = 55,
kExherbo = 56,
kFedora = 57,
kFeren = 58,
kFrugalware = 59,
kFuntoo = 60,
kGalliumOs = 61,
kGaruda = 62,
kGentoo = 63,
kGlaucus = 64,
kGnewSense = 65,
kGnome = 66,
kGoboLinux = 67,
kGrombyang = 68,
kHash = 69,
kHuayra = 70,
kHydroOs = 71,
kHyperbola = 72,
kIglu = 73,
kInstantOs = 74,
kItc = 75,
kJanus = 76,
kKaOs = 77,
kKaisen = 78,
kKali = 79,
kKde = 80,
kKibojoe = 81,
kKogaion = 82,
kKorora = 83,
kKsLinux = 84,
kKubuntu = 85,
kLangitKetujuh = 86,
kLaxerOs = 87,
kLede = 88,
kLibreElec = 89,
kLinuxLite = 90,
kLinuxMint = 91,
kLiveRaizo = 92,
kLmde = 93,
kLubuntu = 94,
kLunar = 95,
kMageia = 96,
kMagpieOs = 97,
kMandriva = 98,
kManjaro = 99,
kMaui = 100,
kMer = 101,
kMinix = 102,
kMx = 103,
kNamib = 104,
kNeptune = 105,
kNetrunner = 106,
kNitrux = 107,
kNixOs = 108,
kNurunner = 109,
kNutyX = 110,
kObRevenge = 111,
kObarun = 112,
kOpenEuler = 113,
kOpenIndiana = 114,
kOpenMandriva = 115,
kOpenSourceMediaCenter = 116,
kOpenStage = 117,
kOpenSuse = 118,
kOpenSuseLeap = 119,
kOpenSuseTumbleweed = 120,
kOpenWrt = 121,
kOpenMamba = 122,
kOracle = 123,
kOsElbrus = 124,
kParabola = 125,
kPardus = 126,
kParrot = 127,
kParsix = 128,
kPcLinuxOs = 129,
kPengwin = 130,
kPentoo = 131,
kPeppermint = 132,
kPisi = 133,
kPnmLinux = 134,
kPopOs = 135,
kPorteus = 136,
kPostMarketOs = 137,
kProxmox = 138,
kPuffOs = 139,
kPuppy = 140,
kPureOs = 141,
kQubes = 142,
kQubyt = 143,
kQuibian = 144,
kRadix = 145,
kRaspbian = 146,
kReborn = 147,
kRedStar = 148,
kRedcore = 149,
kRedhat = 150,
kRefractedDevuan = 151,
kRegata = 152,
kRegolith = 153,
kRocky = 154,
kRosa = 155,
kSabayon = 156,
kSabotage = 157,
kSailfish = 158,
kSalentOs = 159,
kScientific = 160,
kSemc = 161,
kSeptor = 162,
kSerene = 163,
kSharkLinux = 164,
kSiduction = 165,
kSkiffOs = 166,
kSlackware = 167,
kSliTaz = 168,
kSmartOs = 169,
kSolus = 170,
kSourceMage = 171,
kSparky = 172,
kStar = 173,
kSteamOs = 174,
kSwagArch = 175,
kT2 = 176,
kTails = 177,
kTeArch = 178,
kTrisquel = 179,
kUbuntu = 180,
kUnivention = 181,
kVenom = 182,
kVnux = 183,
kVoid = 184,
kXferience = 185,
kXubuntu = 186,
kZorin = 187,
// Needed for UMA.
kMaxValue = kZorin,
};
enum UMALinuxGlibcVersion : uint32_t {
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.
};
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)
}
#if BUILDFLAG(IS_LINUX)
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());
}
// Some releases may have multiple names like "opensuse_leap", "opensuse leap",
// or "opensuseleap". Trim non-alphanumeric characters so they all map to the
// same name.
std::string TrimLinuxDistro(const std::string& distro) {
std::string trimmed;
for (char c : distro) {
if (base::IsAsciiAlphaNumeric(c)) {
trimmed.push_back(c);
}
}
return trimmed;
}
void RecordLinuxDistro() {
std::string distro = base::GetLinuxDistro();
if (distro.empty() || distro == "Unknown") {
base::UmaHistogramEnumeration("Linux.Distro3", UmaLinuxDistro::kUnknown);
return;
}
std::vector<std::string> distro_tokens =
base::SplitString(distro, base::kWhitespaceASCII, base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
CHECK(distro_tokens.size());
if (distro_tokens[0] == "Ubuntu") {
// Format: Ubuntu YY.MM.P [LTS]
// We are only concerned with release (YY.MM) not the patch (P).
if (distro_tokens.size() >= 2) {
RecordLinuxDistroSpecific(distro_tokens[1], 2, "Linux.Distro.Ubuntu");
}
} else if (distro_tokens[0] == "openSUSE") {
// Format: openSUSE Leap RR.R
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).
if (distro_tokens.size() >= 3) {
RecordLinuxDistroSpecific(distro_tokens[2], 1, "Linux.Distro.Debian");
}
} else if (distro_tokens[0] == "Fedora") {
// Format: Fedora RR (<codename>)
if (distro_tokens.size() >= 2) {
RecordLinuxDistroSpecific(distro_tokens[1], 1, "Linux.Distro.Fedora");
}
} else if (distro_tokens.size() >= 2 && distro_tokens[1] == "Mint") {
// Format: Linux Mint RR
if (distro_tokens.size() >= 3) {
RecordLinuxDistroSpecific(distro_tokens[2], 1, "Linux.Distro.Mint");
}
}
using enum UmaLinuxDistro;
// This array must be kept sorted since it is binary searched.
constexpr std::pair<const char*, UmaLinuxDistro> kDistroPrefixes[] = {
{"alma", kAlma},
{"alpine", kAlpine},
{"alter", kAlter},
{"amazon", kAmazon},
{"anarchy", kAnarchy},
{"antergos", kAntergos},
{"antix", kAntiX},
{"aoscos", kAoscOs},
{"aperio", kAperio},
{"apricity", kApricity},
{"arch", kArch},
{"arcolinux", kArcoLinux},
{"artix", kArtix},
{"arya", kArya},
{"asteroidos", kAsteroidOs},
{"ataraxia", kJanus},
{"bedrock", kBedrock},
{"bitrig", kBitrig},
{"blackarch", kBlackArch},
{"blag", kBlag},
{"blankon", kBlankOn},
{"bluelight", kBlueLight},
{"bodhi", kBodhi},
{"bonsai", kBonsai},
{"bunsenlabs", kBunsenLabs},
{"calculate", kCalculate},
{"carbs", kCarbs},
{"cblmariner", kCblMariner},
{"celos", kCelOs},
{"centos", kCentOs},
{"chakra", kChakra},
{"chaletos", kChaletOs},
{"chapeau", kChapeau},
{"cleanjaro", kCleanjaro},
{"clearlinux", kClearLinux},
{"clearos", kClearOs},
{"clover", kClover},
{"condres", kCondres},
{"containerlinux", kContainerLinux},
{"crux", kCrux},
{"crystallinux", kCrystalLinux},
{"cucumber", kCucumber},
{"cyberos", kCyberOs},
{"dahlia", kDahlia},
{"darkos", kDarkOs},
{"debian", kDebian},
{"deepin", kDeepin},
{"desaos", kDesaOs},
{"devuan", kDevuan},
{"dracos", kDracOs},
{"drauger", kDrauger},
{"elementary", kElementary},
{"endeavouros", kEndeavourOs},
{"endless", kEndless},
{"eurolinux", kEuroLinux},
{"exherbo", kExherbo},
{"fedora", kFedora},
{"feren", kFeren},
{"frugalware", kFrugalware},
{"funtoo", kFuntoo},
{"galliumos", kGalliumOs},
{"garuda", kGaruda},
{"gentoo", kGentoo},
{"glaucus", kGlaucus},
{"gnewsense", kGnewSense},
{"gnome", kGnome},
{"gobolinux", kGoboLinux},
{"grombyang", kGrombyang},
{"hash", kHash},
{"huayra", kHuayra},
{"hyperbola", kHyperbola},
{"i3buntu", kUbuntu},
{"iglu", kIglu},
{"instantos", kInstantOs},
{"itc", kItc},
{"janus", kJanus},
{"kaisen", kKaisen},
{"kali", kKali},
{"kaos", kKaOs},
{"kde", kKde},
{"kibojoe", kKibojoe},
{"kogaion", kKogaion},
{"korora", kKorora},
{"kslinux", kKsLinux},
{"kubuntu", kKubuntu},
{"langitketujuh", kLangitKetujuh},
{"laxeros", kLaxerOs},
{"lede", kLede},
{"libreelec", kLibreElec},
{"linuxlite", kLinuxLite},
{"linuxmint", kLinuxMint},
{"liveraizo", kLiveRaizo},
{"lmde", kLmde},
{"lubuntu", kLubuntu},
{"lunar", kLunar},
{"mageia", kMageia},
{"magpieos", kMagpieOs},
{"mandrake", kMandriva},
{"mandriva", kMandriva},
{"manjaro", kManjaro},
{"maui", kMaui},
{"mer", kMer},
{"minix", kMinix},
{"mint", kLinuxMint},
{"mx", kMx},
{"namib", kNamib},
{"neptune", kNeptune},
{"netrunner", kNetrunner},
{"nitrux", kNitrux},
{"nixos", kNixOs},
{"nurunner", kNurunner},
{"nutyx", kNutyX},
{"obarun", kObarun},
{"obrevenge", kObRevenge},
{"openeuler", kOpenEuler},
{"openindiana", kOpenIndiana},
{"openmamba", kOpenMamba},
{"openmandriva", kOpenMandriva},
{"opensourcemediacenter", kOpenSourceMediaCenter},
{"openstage", kOpenStage},
{"opensuse", kOpenSuse},
{"opensuseleap", kOpenSuseLeap},
{"opensusetumbleweed", kOpenSuseTumbleweed},
{"openwrt", kOpenWrt},
{"oracle", kOracle},
{"oselbrus", kOsElbrus},
{"osmc", kOpenSourceMediaCenter},
{"parabola", kParabola},
{"pardus", kPardus},
{"parrot", kParrot},
{"parsix", kParsix},
{"pclinuxos", kPcLinuxOs},
{"pengwin", kPengwin},
{"pentoo", kPentoo},
{"peppermint", kPeppermint},
{"pisi", kPisi},
{"pnmlinux", kPnmLinux},
{"popos", kPopOs},
{"porteus", kPorteus},
{"postmarketos", kPostMarketOs},
{"precisepuppy", kPuppy},
{"proxmox", kProxmox},
{"puffos", kPuffOs},
{"puppy", kPuppy},
{"pureos", kPureOs},
{"qubes", kQubes},
{"qubyt", kQubyt},
{"quibian", kQuibian},
{"quirkywerewolf", kPuppy},
{"radix", kRadix},
{"raspbian", kRaspbian},
{"reborn", kReborn},
{"redcore", kRedcore},
{"redhat", kRedhat},
{"redstar", kRedStar},
{"refracteddevuan", kRefractedDevuan},
{"regata", kRegata},
{"regolith", kRegolith},
{"rhel", kRedhat},
{"rocky", kRocky},
{"rosa", kRosa},
{"sabayon", kSabayon},
{"sabotage", kSabotage},
{"sailfish", kSailfish},
{"salentos", kSalentOs},
{"scientific", kScientific},
{"semc", kSemc},
{"septor", kSeptor},
{"serene", kSerene},
{"sharklinux", kSharkLinux},
{"siduction", kSiduction},
{"skiffos", kSkiffOs},
{"slackware", kSlackware},
{"slitaz", kSliTaz},
{"smartos", kSmartOs},
{"solus", kSolus},
{"sourcemage", kSourceMage},
{"sparky", kSparky},
{"star", kStar},
{"steamos", kSteamOs},
{"suse", kOpenSuse},
{"swagarch", kSwagArch},
{"t2", kT2},
{"tails", kTails},
{"tearch", kTeArch},
{"trisquel", kTrisquel},
{"ubuntu", kUbuntu},
{"univention", kUnivention},
{"venom", kVenom},
{"vnux", kVnux},
{"void", kVoid},
{"whpnmlinux", kPnmLinux},
{"xferience", kXferience},
{"xubuntu", kXubuntu},
{"zorin", kZorin},
};
struct Compare {
bool operator()(const std::string& string,
const std::pair<const char*, UmaLinuxDistro>& pair) {
return string < pair.first;
}
};
std::string trimmed = TrimLinuxDistro(base::ToLowerASCII(distro));
auto* it = std::upper_bound(kDistroPrefixes, std::end(kDistroPrefixes),
trimmed, Compare());
if (it != kDistroPrefixes &&
base::StartsWith(trimmed, (UNSAFE_TODO(--it))->first)) {
base::UmaHistogramEnumeration("Linux.Distro3", it->second);
} else {
base::UmaHistogramEnumeration("Linux.Distro3", UmaLinuxDistro::kOther);
}
}
#endif // BUILDFLAG(IS_LINUX)
void RecordLinuxGlibcVersion() {
#if defined(__GLIBC__) && BUILDFLAG(IS_LINUX)
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
}
#if BUILDFLAG(IS_WIN)
// Record the UMA histogram when a response is received.
void OnIsPinnedToTaskbarResult(bool succeeded, bool is_pinned_to_taskbar) {
// Used for histograms; do not reorder.
enum Result { NOT_PINNED = 0, PINNED = 1, FAILURE = 2, NUM_RESULTS };
Result result = FAILURE;
if (succeeded)
result = is_pinned_to_taskbar ? PINNED : NOT_PINNED;
base::UmaHistogramEnumeration("Windows.IsPinnedToTaskbar", result,
NUM_RESULTS);
// If Chrome is not pinned to taskbar, clear the recording that the installer
// pinned Chrome to the taskbar, so that if the user pins Chrome back to the
// taskbar, we don't count launches as coming from an installer-pinned
// shortcut. TODO(crbug.com/40235395): We currently only check if
// Chrome is pinned to the taskbar 1 out every 100 launches, which makes this
// less meaningful, so if keeping track of whether the installer pinned Chrome
// to the taskbar is important, we need to deal with that.
// Record whether or not the user unpinned an installer pin of Chrome. Records
// true if the installer pinned Chrome, and it's not pinned on this startup,
// false if the installer pinned Chrome, and it's still pinned.
if (GetInstallerPinnedChromeToTaskbar().value_or(false)) {
if (result == NOT_PINNED)
SetInstallerPinnedChromeToTaskbar(false);
if (result != FAILURE) {
base::UmaHistogramBoolean("Windows.InstallerPinUnpinned",
result == NOT_PINNED);
}
}
}
// 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(&OnIsPinnedToTaskbarResult));
}
// This registry key is not fully documented but there is information on it
// here:
// https://blogs.blackberry.com/en/2017/10/windows-10-parallel-loading-breakdown.
bool IsParallelDllLoadingEnabled() {
base::FilePath exe_path;
if (!base::PathService::Get(base::FILE_EXE, &exe_path))
return false;
const wchar_t kIFEOKey[] =
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution "
L"Options\\";
std::wstring browser_process_key = kIFEOKey + exe_path.BaseName().value();
base::win::RegKey key;
if (ERROR_SUCCESS != key.Open(HKEY_LOCAL_MACHINE, browser_process_key.c_str(),
KEY_QUERY_VALUE))
return true;
const wchar_t kMaxLoaderThreads[] = L"MaxLoaderThreads";
DWORD max_loader_threads = 0;
if (ERROR_SUCCESS != key.ReadValueDW(kMaxLoaderThreads, &max_loader_threads))
return true;
// Note: If LoaderThreads is 0, it will be set to the default value of 4.
return max_loader_threads != 1;
}
// Records the presence (bad) or absence (good) of AcLayers.dll in the browser
// process.
void RecordAppCompatMetrics() {
HMODULE mod = ::GetModuleHandleW(L"AcLayers.dll");
base::UmaHistogramBoolean("Windows.AcLayersLoaded", !!mod);
}
void RecordWin11HardwareRequirementsMetrics(
const base::win::HardwareEvaluationResult& result) {
base::UmaHistogramBoolean("Windows.Win11UpgradeEligible",
result.IsEligible());
base::UmaHistogramBoolean("Windows.Win11HardwareRequirements.CPUCheck",
result.cpu);
base::UmaHistogramBoolean("Windows.Win11HardwareRequirements.MemoryCheck",
result.memory);
base::UmaHistogramBoolean("Windows.Win11HardwareRequirements.DiskCheck",
result.disk);
base::UmaHistogramBoolean("Windows.Win11HardwareRequirements.FirmwareCheck",
result.firmware);
base::UmaHistogramBoolean("Windows.Win11HardwareRequirements.TPMCheck",
result.tpm);
}
void MaybeRecordOneDriveSyncMetrics() {
if (!base::FeatureList::IsEnabled(
cloud_synced_folder_checker::features::kCloudSyncedFolderChecker)) {
return;
}
cloud_synced_folder_checker::CloudSyncStatus status =
cloud_synced_folder_checker::EvaluateOneDriveSyncStatus();
base::UmaHistogramBoolean("Windows.OneDriveSyncState.Synced",
status.synced());
base::UmaHistogramBoolean("Windows.OneDriveSyncState.DesktopSynced",
status.desktop_synced());
base::UmaHistogramBoolean("Windows.OneDriveSyncState.DocumentsSynced",
status.documents_synced());
}
#endif // BUILDFLAG(IS_WIN)
void RecordDisplayHDRStatus(const display::Display& display) {
base::UmaHistogramBoolean("Hardware.Display.SupportsHDR",
display.GetColorSpaces().SupportsHDR());
}
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
// Records whether Chrome is the default PDF viewer.
void RecordDefaultPdfViewerState() {
#if BUILDFLAG(IS_MAC)
auto is_default_callback = base::BindOnce(
&shell_integration::IsDefaultHandlerForUTType, "com.adobe.pdf");
#elif BUILDFLAG(IS_WIN)
auto is_default_callback = base::BindOnce(
&shell_integration::IsDefaultHandlerForFileExtension, ".pdf");
#else
#error Unsupported platform
#endif
auto record_default_state_callback =
std::move(is_default_callback)
.Then(base::BindOnce(
[](shell_integration::DefaultWebClientState default_state) {
base::UmaHistogramEnumeration(
"PDF.DefaultState", default_state,
shell_integration::NUM_DEFAULT_STATES);
}));
base::ThreadPool::PostTask(FROM_HERE, {base::MayBlock()},
std::move(record_default_state_callback));
}
#endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_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 BUILDFLAG(IS_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());
base::UmaHistogramBoolean("Windows.HasThreadTicks",
base::ThreadTicks::IsSupported());
// Determine whether parallel DLL loading is enabled for the browser process
// executable. This is disabled by default on fresh Windows installations, but
// the registry key that controls this might have been removed. Having the
// parallel DLL loader enabled might affect both sandbox and early startup
// behavior.
base::UmaHistogramBoolean("Windows.ParallelDllLoadingEnabled",
IsParallelDllLoadingEnabled());
RecordAppCompatMetrics();
MaybeRecordOneDriveSyncMetrics();
if (base::win::OSInfo::Kernel32Version() < base::win::Version::WIN11) {
base::win::HardwareEvaluationResult result =
base::win::EvaluateWin11HardwareRequirements();
RecordWin11HardwareRequirementsMetrics(result);
}
key_credential_manager_support::ReportKeyCredentialManagerSupport();
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
crypto::UnexportableKeyProvider::Config config;
#if BUILDFLAG(IS_MAC)
config.keychain_access_group = kUnexportableKeysKeychainAccessGroup;
#endif // BUILDFLAG(IS_MAC)
crypto::MaybeMeasureTpmOperations(std::move(config));
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
// Record whether Chrome is the default browser or not.
// Disabled on Linux due to hanging browser tests, see crbug.com/1216328.
#if !BUILDFLAG(IS_LINUX)
shell_integration::DefaultWebClientState default_state =
shell_integration::GetDefaultBrowser();
base::UmaHistogramEnumeration("DefaultBrowser.State", default_state,
shell_integration::NUM_DEFAULT_STATES);
#endif // !BUILDFLAG(IS_LINUX)
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
// Record whether Chrome is the default PDF viewer.
RecordDefaultPdfViewerState();
#endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_MAC)
base::mac::ProcessRequirement::MaybeGatherMetrics();
#endif
}
} // namespace
#if BUILDFLAG(IS_ANDROID)
bool IsBundleForMixedDeviceAccordingToVersionCode(
const std::string& version_code) {
// Primary bitness of the bundle is encoded in the last digit of the version
// code. And the variant (package name) is encoded in the second to last.
//
// From build/util/android_chrome_version.py:
// 'arm': {
// '32': 0,
// '32_64': 1,
// '64_32': 2,
// '64_32_high': 3,
// '64': 4,
// },
// 'intel': {
// '32': 6,
// '32_64': 7,
// '64_32': 8,
// '64': 9,
// },
//
// _PACKAGE_NAMES = {
// 'CHROME': 0,
// 'CHROME_MODERN': 10,
// 'MONOCHROME': 20,
// 'TRICHROME': 30,
// [...]
if (version_code.length() < 2) {
return false;
}
// '32' and '64' bundles go on 32bit-only and 64bit-only devices, so exclude
// them.
std::set<char> arch_codes_mixed = {'1', '2', '3', '7', '8'};
char arch_code = version_code.back();
// Only 'TRICHROME' supports 64-bit.
constexpr char kTriChromeVariant = '3';
char variant = version_code[version_code.length() - 2];
return arch_codes_mixed.count(arch_code) > 0 && variant == kTriChromeVariant;
}
#endif // BUILDFLAG(IS_ANDROID)
ChromeBrowserMainExtraPartsMetrics::ChromeBrowserMainExtraPartsMetrics()
: display_count_(0) {}
ChromeBrowserMainExtraPartsMetrics::~ChromeBrowserMainExtraPartsMetrics() =
default;
void ChromeBrowserMainExtraPartsMetrics::PreCreateThreads() {
// Initialize the TabStatsTracker singleton instance. Must be initialized
// before `responsiveness::Watcher`, which happens in
// BrowserMainLoop::PreMainMessageLoopRun(), thus the decision to use
// `PreCreateThreads`.
// 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()));
}
}
void ChromeBrowserMainExtraPartsMetrics::PostCreateMainMessageLoop() {
#if !BUILDFLAG(IS_ANDROID)
// Must be initialized before any child processes are spawned.
process_monitor_ = std::make_unique<ProcessMonitor>();
#endif // !BUILDFLAG(IS_ANDROID)
}
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_OPTIMIZED)
#if BUILDFLAG(USE_THIN_LTO)
"EnabledWithThinLTO"
#else
"Enabled"
#endif
#else
"Disabled"
#endif
);
// Records whether or not the Segment heap is in use.
#if BUILDFLAG(IS_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");
}
#endif // BUILDFLAG(IS_WIN)
// Register synthetic Finch trials proposed by PartitionAlloc.
auto pa_trials = base::allocator::ProposeSyntheticFinchTrials();
for (auto& trial : pa_trials) {
auto [trial_name, group_name] = trial;
ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(trial_name,
group_name);
}
}
void ChromeBrowserMainExtraPartsMetrics::PostBrowserStart() {
RecordMemoryMetricsAfterDelay();
RecordLinuxGlibcVersion();
constexpr base::TaskTraits kBestEffortTaskTraits = {
base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN};
#if BUILDFLAG(IS_LINUX)
base::ThreadPool::PostTask(FROM_HERE, kBestEffortTaskTraits,
base::BindOnce(&RecordLinuxDistro));
#endif
#if BUILDFLAG(IS_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 // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_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::Seconds(45));
}
#endif // BUILDFLAG(IS_WIN)
auto* screen = display::Screen::Get();
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 !BUILDFLAG(IS_ANDROID)
// In ChromeOS, the chrome application typically starts at the login screen and
// waits for the user to log in before opening a browser window, so calling
// `BeginFirstWebContentsProfiling()` is inappropriate because the
// `BrowserList` is typically empty at this point. Similarly, a restart after a
// crash (which has no login screen) requires the user to click a notification
// prompt before browser windows are restored, so the `BrowserList` is also
// empty in this case.
#if !BUILDFLAG(IS_CHROMEOS)
metrics::BeginFirstWebContentsProfiling();
#endif // !BUILDFLAG(IS_CHROMEOS)
// Instantiate the power-related metrics reporters.
// BatteryDischargeRateReporter reports the system-wide battery discharge
// rate. It depends on the TabStatsTracker to determine the usage scenario,
// and the BatteryStateSampler to determine the battery level.
// The TabStatsTracker always exists (except during unit tests), while the
// BatteryStateSampler only exists on platform where a BatteryLevelProvider
// implementation exists.
if (metrics::TabStatsTracker::HasInstance() &&
base::BatteryStateSampler::Get()) {
battery_discharge_reporter_ = std::make_unique<BatteryDischargeReporter>(
base::BatteryStateSampler::Get());
}
// PowerMetricsReporter focus solely on Chrome-specific metrics that affect
// power (CPU time, wake ups, etc.). Only instantiate it if |process_monitor_|
// exists. This is always the case for Chrome but not for the unit tests.
if (process_monitor_) {
power_metrics_reporter_ =
std::make_unique<PowerMetricsReporter>(process_monitor_.get());
}
performance_intervention_metrics_reporter_ =
std::make_unique<PerformanceInterventionMetricsReporter>(
g_browser_process->local_state());
web_app_metrics_provider_ =
std::make_unique<web_app::SamplingMetricsProvider>();
#endif // !BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_LINUX)
pressure_metrics_reporter_ = std::make_unique<PressureMetricsReporter>();
#endif // BUILDFLAG(IS_LINUX)
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
base::trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(
power_metrics::SystemPowerMonitor::GetInstance());
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
HandleEnableBenchmarkingCountdownAsync();
}
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::PostDestroyThreads() {
if (metrics::TabStatsTracker::HasInstance()) {
// responsiveness::Watcher currently outlives TabStatsTracker and
// RemoveObserver is never called (see UsageScenarioTracker). This should be
// considered/addressed if refining Watcher's lifetime or migrating
// TabStatsTracker away from global state, as this could lead to a dangling
// pointer or similar.
metrics::TabStatsTracker::ClearInstance();
}
#if !BUILDFLAG(IS_ANDROID)
// Reset the pointer to `performance_intervention_metrics_reporter_` to ensure
// that PrefService outlives the metrics reporter to prevent the reporter from
// holding a dangling pointer.
performance_intervention_metrics_reporter_.reset();
#endif // !BUILDFLAG(IS_ANDROID)
}
void ChromeBrowserMainExtraPartsMetrics::RegisterPrefs(
PrefRegistrySimple* registry) {
registry->RegisterIntegerPref(kEnableBenchmarkingPrefId,
kEnableBenchmarkingCountdownDefault);
}
void ChromeBrowserMainExtraPartsMetrics::HandleEnableBenchmarkingCountdown(
PrefService* pref_service,
std::unique_ptr<flags_ui::FlagsStorage> storage,
flags_ui::FlagAccess access) {
std::set<std::string> flags = storage->GetFlags();
// The implicit assumption here is that chrome://flags are stored in
// flags_ui::PrefServiceFlagsStorage and the multi-value switch has format
// enable-benchmarking@<n>.
std::string prefix =
base::StrCat({variations::switches::kEnableBenchmarking, "@"});
auto it = std::find_if(
flags.begin(), flags.end(),
[&prefix](std::string flag) { return base::StartsWith(flag, prefix); });
if (it == flags.end()) {
return;
}
int countdown = pref_service->GetInteger(kEnableBenchmarkingPrefId);
countdown--;
if (countdown <= 0) {
// Clear the countdown pref.
pref_service->ClearPref(kEnableBenchmarkingPrefId);
// Clear the flag storage.
flags.erase(it);
storage->SetFlags(std::move(flags));
} else {
pref_service->SetInteger(kEnableBenchmarkingPrefId, countdown);
}
}
void ChromeBrowserMainExtraPartsMetrics::
HandleEnableBenchmarkingCountdownAsync() {
Profile* profile = nullptr;
#if BUILDFLAG(IS_CHROMEOS)
// This logic is subtle. There are two ways for PostBrowserStart to be called
// on ChromeOS. The first is when the device first shows the login screen. In
// this case the profile is the login profile. The second is after the user
// logs in. If any flags have been changed from the login profile's flags,
// then all of ash is restarted. We only care about invoking this logic in the
// second case. Thus we check if IsUserLoggedIn() to guard the logic.
if (!user_manager::UserManager::IsInitialized() ||
!user_manager::UserManager::Get()->IsUserLoggedIn()) {
return;
}
profile = g_browser_process->profile_manager()->GetPrimaryUserProfile();
#endif
about_flags::GetStorage(profile,
base::BindOnce(&HandleEnableBenchmarkingCountdown,
g_browser_process->local_state()));
}
void ChromeBrowserMainExtraPartsMetrics::OnDisplayAdded(
const display::Display& new_display) {
EmitDisplaysChangedMetric();
RecordDisplayHDRStatus(new_display);
}
void ChromeBrowserMainExtraPartsMetrics::OnDisplaysRemoved(
const display::Displays& removed_displays) {
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::Get()->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