Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/startup_metric_utils/browser/startup_metric_utils.h" | 5 #include "components/startup_metric_utils/browser/startup_metric_utils.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <stdint.h> | |
| 8 | 9 |
| 9 #include <memory> | 10 #include <memory> |
| 11 #include <string> | |
| 10 | 12 |
| 11 #include "base/containers/hash_tables.h" | 13 #include "base/containers/hash_tables.h" |
| 12 #include "base/environment.h" | 14 #include "base/environment.h" |
| 13 #include "base/lazy_instance.h" | 15 #include "base/lazy_instance.h" |
| 14 #include "base/logging.h" | 16 #include "base/logging.h" |
| 15 #include "base/metrics/histogram.h" | 17 #include "base/metrics/histogram.h" |
| 16 #include "base/metrics/histogram_macros.h" | 18 #include "base/metrics/histogram_macros.h" |
| 17 #include "base/process/process_info.h" | 19 #include "base/process/process_info.h" |
| 18 #include "base/strings/string_number_conversions.h" | 20 #include "base/strings/string_number_conversions.h" |
| 19 #include "base/sys_info.h" | 21 #include "base/sys_info.h" |
| 20 #include "base/threading/platform_thread.h" | 22 #include "base/threading/platform_thread.h" |
| 21 #include "base/trace_event/trace_event.h" | 23 #include "base/trace_event/trace_event.h" |
| 22 #include "build/build_config.h" | 24 #include "build/build_config.h" |
| 23 #include "components/prefs/pref_registry_simple.h" | 25 #include "components/prefs/pref_registry_simple.h" |
| 24 #include "components/prefs/pref_service.h" | 26 #include "components/prefs/pref_service.h" |
| 25 #include "components/startup_metric_utils/browser/pref_names.h" | 27 #include "components/startup_metric_utils/browser/pref_names.h" |
| 26 #include "components/version_info/version_info.h" | 28 #include "components/version_info/version_info.h" |
| 27 | 29 |
| 28 #if defined(OS_WIN) | 30 #if defined(OS_WIN) |
| 29 #include <winternl.h> | 31 #include <winternl.h> |
| 30 #include "base/win/win_util.h" | 32 #include "base/win/win_util.h" |
| 31 #include "base/win/windows_version.h" | |
| 32 #endif | 33 #endif |
| 33 | 34 |
| 34 namespace startup_metric_utils { | 35 namespace startup_metric_utils { |
| 35 | 36 |
| 36 namespace { | 37 namespace { |
| 37 | 38 |
| 38 // Mark as volatile to defensively make sure usage is thread-safe. | 39 // Mark as volatile to defensively make sure usage is thread-safe. |
| 39 // Note that at the time of this writing, access is only on the UI thread. | 40 // Note that at the time of this writing, access is only on the UI thread. |
| 40 volatile bool g_non_browser_ui_displayed = false; | 41 volatile bool g_non_browser_ui_displayed = false; |
| 41 | 42 |
| 42 base::LazyInstance<base::TimeTicks>::Leaky g_process_creation_ticks = | 43 base::LazyInstance<base::TimeTicks>::Leaky g_process_creation_ticks = |
| 43 LAZY_INSTANCE_INITIALIZER; | 44 LAZY_INSTANCE_INITIALIZER; |
| 44 | 45 |
| 45 base::LazyInstance<base::TimeTicks>::Leaky g_browser_main_entry_point_ticks = | 46 base::LazyInstance<base::TimeTicks>::Leaky g_browser_main_entry_point_ticks = |
| 46 LAZY_INSTANCE_INITIALIZER; | 47 LAZY_INSTANCE_INITIALIZER; |
| 47 | 48 |
| 48 base::LazyInstance<base::TimeTicks>::Leaky g_renderer_main_entry_point_ticks = | 49 base::LazyInstance<base::TimeTicks>::Leaky g_renderer_main_entry_point_ticks = |
| 49 LAZY_INSTANCE_INITIALIZER; | 50 LAZY_INSTANCE_INITIALIZER; |
| 50 | 51 |
| 51 // Only used by RecordMainEntryTimeHistogram(), should go away with it (do not | 52 // Only used by RecordMainEntryTimeHistogram(), should go away with it (do not |
| 52 // add new uses of this), see crbug.com/317481 for discussion on why it was kept | 53 // add new uses of this), see crbug.com/317481 for discussion on why it was kept |
| 53 // as-is for now. | 54 // as-is for now. |
| 54 base::LazyInstance<base::Time>::Leaky g_browser_main_entry_point_time = | 55 base::LazyInstance<base::Time>::Leaky g_browser_main_entry_point_time = |
| 55 LAZY_INSTANCE_INITIALIZER; | 56 LAZY_INSTANCE_INITIALIZER; |
| 56 | 57 |
| 58 // An enumeration of startup temperatures. This must be kept in sync with the | |
| 59 // UMA StartupType enumeration defined in histograms.xml. | |
| 60 enum StartupTemperature { | |
| 61 // The startup was a cold start: nearly all of the binaries and resources were | |
| 62 // brought into memory using hard faults. | |
| 63 COLD_STARTUP_TEMPERATURE = 0, | |
| 64 // The startup was a warm start: the binaries and resources were mostly | |
| 65 // already resident in memory and effectively no hard faults were observed. | |
| 66 WARM_STARTUP_TEMPERATURE = 1, | |
| 67 // The startup type couldn't quite be classified as warm or cold, but rather | |
| 68 // was somewhere in between. | |
| 69 LUKEWARM_STARTUP_TEMPERATURE = 2, | |
| 70 // This must be after all meaningful values. All new values should be added | |
| 71 // above this one. | |
| 72 STARTUP_TEMPERATURE_COUNT, | |
| 73 // Startup temperature wasn't yet determined. | |
| 74 UNDETERMINED_STARTUP_TEMPERATURE | |
| 75 }; | |
| 76 | |
| 57 StartupTemperature g_startup_temperature = UNDETERMINED_STARTUP_TEMPERATURE; | 77 StartupTemperature g_startup_temperature = UNDETERMINED_STARTUP_TEMPERATURE; |
| 58 | 78 |
| 59 constexpr int kUndeterminedStartupsWithCurrentVersion = 0; | 79 constexpr int kUndeterminedStartupsWithCurrentVersion = 0; |
| 60 int g_startups_with_current_version = kUndeterminedStartupsWithCurrentVersion; | 80 int g_startups_with_current_version = kUndeterminedStartupsWithCurrentVersion; |
| 61 | 81 |
| 62 #if defined(OS_WIN) | 82 #if defined(OS_WIN) |
| 63 | 83 |
| 64 // These values are taken from the Startup.BrowserMessageLoopStartHardFaultCount | 84 // These values are taken from the Startup.BrowserMessageLoopStartHardFaultCount |
| 65 // histogram. If the cold start histogram starts looking strongly bimodal it may | 85 // histogram. If the cold start histogram starts looking strongly bimodal it may |
| 66 // be because the binary/resource sizes have grown significantly larger than | 86 // be because the binary/resource sizes have grown significantly larger than |
| 67 // when these values were set. In this case the new values need to be chosen | 87 // when these values were set. In this case the new values need to be chosen |
| 68 // from the original histogram. | 88 // from the original histogram. |
| 69 // | 89 // |
| 70 // Maximum number of hard faults tolerated for a startup to be classified as a | 90 // Maximum number of hard faults tolerated for a startup to be classified as a |
| 71 // warm start. Set at roughly the 40th percentile of the HardFaultCount | 91 // warm start. Set at roughly the 40th percentile of the HardFaultCount |
| 72 // histogram. | 92 // histogram. |
| 73 const uint32_t WARM_START_HARD_FAULT_COUNT_THRESHOLD = 5; | 93 constexpr uint32_t kWarmStartHardFaultCountThreshold = 5; |
| 74 // Minimum number of hard faults expected for a startup to be classified as a | 94 // Minimum number of hard faults expected for a startup to be classified as a |
| 75 // cold start. Set at roughly the 60th percentile of the HardFaultCount | 95 // cold start. Set at roughly the 60th percentile of the HardFaultCount |
| 76 // histogram. | 96 // histogram. |
| 77 const uint32_t COLD_START_HARD_FAULT_COUNT_THRESHOLD = 1200; | 97 constexpr uint32_t kColdStartHardFaultCountThreshold = 1200; |
| 78 | 98 |
| 79 // The struct used to return system process information via the NT internal | 99 // The struct used to return system process information via the NT internal |
| 80 // QuerySystemInformation call. This is partially documented at | 100 // QuerySystemInformation call. This is partially documented at |
| 81 // http://goo.gl/Ja9MrH and fully documented at http://goo.gl/QJ70rn | 101 // http://goo.gl/Ja9MrH and fully documented at http://goo.gl/QJ70rn |
| 82 // This structure is laid out in the same format on both 32-bit and 64-bit | 102 // This structure is laid out in the same format on both 32-bit and 64-bit |
| 83 // systems, but has a different size due to the various pointer-sized fields. | 103 // systems, but has a different size due to the various pointer-sized fields. |
| 84 struct SYSTEM_PROCESS_INFORMATION_EX { | 104 struct SYSTEM_PROCESS_INFORMATION_EX { |
| 85 ULONG NextEntryOffset; | 105 ULONG NextEntryOffset; |
| 86 ULONG NumberOfThreads; | 106 ULONG NumberOfThreads; |
| 87 LARGE_INTEGER WorkingSetPrivateSize; | 107 LARGE_INTEGER WorkingSetPrivateSize; |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 98 PVOID Reserved5[11]; | 118 PVOID Reserved5[11]; |
| 99 SIZE_T PeakPagefileUsage; | 119 SIZE_T PeakPagefileUsage; |
| 100 SIZE_T PrivatePageCount; | 120 SIZE_T PrivatePageCount; |
| 101 LARGE_INTEGER Reserved6[6]; | 121 LARGE_INTEGER Reserved6[6]; |
| 102 // Array of SYSTEM_THREAD_INFORMATION structs follows. | 122 // Array of SYSTEM_THREAD_INFORMATION structs follows. |
| 103 }; | 123 }; |
| 104 | 124 |
| 105 // The signature of the NtQuerySystemInformation function. | 125 // The signature of the NtQuerySystemInformation function. |
| 106 typedef NTSTATUS (WINAPI *NtQuerySystemInformationPtr)( | 126 typedef NTSTATUS (WINAPI *NtQuerySystemInformationPtr)( |
| 107 SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); | 127 SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); |
| 128 | |
| 129 // Gets the hard fault count of the current process through |hard_fault_count|. | |
| 130 // Returns true on success. | |
| 131 bool GetHardFaultCountForCurrentProcess(uint32_t* hard_fault_count) { | |
|
gab
2016/08/31 16:30:45
Assuming cut&paste as-is, not reviewing each line.
fdoray
2016/08/31 19:08:09
cut&paste + removed "if (base::win::GetVersion() <
| |
| 132 DCHECK(hard_fault_count); | |
| 133 | |
| 134 // Get the function pointer. | |
| 135 static const NtQuerySystemInformationPtr query_sys_info = | |
| 136 reinterpret_cast<NtQuerySystemInformationPtr>(::GetProcAddress( | |
| 137 GetModuleHandle(L"ntdll.dll"), "NtQuerySystemInformation")); | |
| 138 if (query_sys_info == nullptr) | |
| 139 return false; | |
| 140 | |
| 141 // The output of this system call depends on the number of threads and | |
| 142 // processes on the entire system, and this can change between calls. Retry | |
| 143 // a small handful of times growing the buffer along the way. | |
| 144 // NOTE: The actual required size depends entirely on the number of processes | |
| 145 // and threads running on the system. The initial guess suffices for | |
| 146 // ~100s of processes and ~1000s of threads. | |
| 147 std::vector<uint8_t> buffer(32 * 1024); | |
| 148 for (size_t tries = 0; tries < 3; ++tries) { | |
| 149 ULONG return_length = 0; | |
| 150 const NTSTATUS status = | |
| 151 query_sys_info(SystemProcessInformation, buffer.data(), | |
| 152 static_cast<ULONG>(buffer.size()), &return_length); | |
| 153 // Insufficient space in the buffer. | |
| 154 if (return_length > buffer.size()) { | |
| 155 buffer.resize(return_length); | |
| 156 continue; | |
| 157 } | |
| 158 if (NT_SUCCESS(status) && return_length <= buffer.size()) | |
| 159 break; | |
| 160 return false; | |
| 161 } | |
| 162 | |
| 163 // Look for the struct housing information for the current process. | |
| 164 const DWORD proc_id = ::GetCurrentProcessId(); | |
| 165 size_t index = 0; | |
| 166 while (index < buffer.size()) { | |
| 167 DCHECK_LE(index + sizeof(SYSTEM_PROCESS_INFORMATION_EX), buffer.size()); | |
| 168 SYSTEM_PROCESS_INFORMATION_EX* proc_info = | |
| 169 reinterpret_cast<SYSTEM_PROCESS_INFORMATION_EX*>(buffer.data() + index); | |
| 170 if (base::win::HandleToUint32(proc_info->UniqueProcessId) == proc_id) { | |
| 171 *hard_fault_count = proc_info->HardFaultCount; | |
| 172 return true; | |
| 173 } | |
| 174 // The list ends when NextEntryOffset is zero. This also prevents busy | |
| 175 // looping if the data is in fact invalid. | |
| 176 if (proc_info->NextEntryOffset <= 0) | |
| 177 return false; | |
| 178 index += proc_info->NextEntryOffset; | |
| 179 } | |
| 180 | |
| 181 return false; | |
| 182 } | |
| 108 #endif // defined(OS_WIN) | 183 #endif // defined(OS_WIN) |
| 109 | 184 |
| 110 #define UMA_HISTOGRAM_TIME_IN_MINUTES_MONTH_RANGE(name, sample) \ | 185 #define UMA_HISTOGRAM_TIME_IN_MINUTES_MONTH_RANGE(name, sample) \ |
| 111 UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, \ | 186 UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, \ |
| 112 base::TimeDelta::FromDays(30).InMinutes(), 50) | 187 base::TimeDelta::FromDays(30).InMinutes(), 50) |
| 113 | 188 |
| 114 // Helper macro for splitting out an UMA histogram based on startup temperature. | 189 // Helper macro for splitting out an UMA histogram based on startup temperature. |
| 115 // |type| is the histogram type, and corresponds to an UMA macro like | 190 // |type| is the histogram type, and corresponds to an UMA macro like |
| 116 // UMA_HISTOGRAM_LONG_TIMES. It must itself be a macro that only takes two | 191 // UMA_HISTOGRAM_LONG_TIMES. It must itself be a macro that only takes two |
| 117 // parameters. | 192 // parameters. |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 272 // Factory properties copied from UMA_HISTOGRAM_CUSTOM_COUNTS macro. | 347 // Factory properties copied from UMA_HISTOGRAM_CUSTOM_COUNTS macro. |
| 273 if (!same_version_startup_count_suffix.empty()) { | 348 if (!same_version_startup_count_suffix.empty()) { |
| 274 base::Histogram::FactoryGet( | 349 base::Histogram::FactoryGet( |
| 275 kHardFaultCountHistogram + same_version_startup_count_suffix, 1, 40000, | 350 kHardFaultCountHistogram + same_version_startup_count_suffix, 1, 40000, |
| 276 50, base::HistogramBase::kUmaTargetedHistogramFlag) | 351 50, base::HistogramBase::kUmaTargetedHistogramFlag) |
| 277 ->Add(hard_fault_count); | 352 ->Add(hard_fault_count); |
| 278 } | 353 } |
| 279 | 354 |
| 280 // Determine the startup type based on the number of observed hard faults. | 355 // Determine the startup type based on the number of observed hard faults. |
| 281 DCHECK_EQ(UNDETERMINED_STARTUP_TEMPERATURE, g_startup_temperature); | 356 DCHECK_EQ(UNDETERMINED_STARTUP_TEMPERATURE, g_startup_temperature); |
| 282 if (hard_fault_count < WARM_START_HARD_FAULT_COUNT_THRESHOLD) { | 357 if (hard_fault_count < kWarmStartHardFaultCountThreshold) { |
| 283 g_startup_temperature = WARM_STARTUP_TEMPERATURE; | 358 g_startup_temperature = WARM_STARTUP_TEMPERATURE; |
| 284 } else if (hard_fault_count >= COLD_START_HARD_FAULT_COUNT_THRESHOLD) { | 359 } else if (hard_fault_count >= kColdStartHardFaultCountThreshold) { |
| 285 g_startup_temperature = COLD_STARTUP_TEMPERATURE; | 360 g_startup_temperature = COLD_STARTUP_TEMPERATURE; |
| 286 } else { | 361 } else { |
| 287 g_startup_temperature = LUKEWARM_STARTUP_TEMPERATURE; | 362 g_startup_temperature = LUKEWARM_STARTUP_TEMPERATURE; |
| 288 } | 363 } |
| 289 | 364 |
| 290 // Record the startup 'temperature'. | 365 // Record the startup 'temperature'. |
| 291 const char kStartupTemperatureHistogram[] = "Startup.Temperature"; | 366 const char kStartupTemperatureHistogram[] = "Startup.Temperature"; |
| 292 UMA_HISTOGRAM_ENUMERATION(kStartupTemperatureHistogram, g_startup_temperature, | 367 UMA_HISTOGRAM_ENUMERATION(kStartupTemperatureHistogram, g_startup_temperature, |
| 293 STARTUP_TEMPERATURE_COUNT); | 368 STARTUP_TEMPERATURE_COUNT); |
| 294 // As well as its suffixed twin. | 369 // As well as its suffixed twin. |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 481 pref_service->SetString(prefs::kLastStartupVersion, current_version); | 556 pref_service->SetString(prefs::kLastStartupVersion, current_version); |
| 482 pref_service->SetInteger(prefs::kSameVersionStartupCount, 1); | 557 pref_service->SetInteger(prefs::kSameVersionStartupCount, 1); |
| 483 } | 558 } |
| 484 | 559 |
| 485 UMA_HISTOGRAM_COUNTS_100("Startup.SameVersionStartupCount", | 560 UMA_HISTOGRAM_COUNTS_100("Startup.SameVersionStartupCount", |
| 486 g_startups_with_current_version); | 561 g_startups_with_current_version); |
| 487 } | 562 } |
| 488 | 563 |
| 489 } // namespace | 564 } // namespace |
| 490 | 565 |
| 491 #if defined(OS_WIN) | |
| 492 bool GetHardFaultCountForCurrentProcess(uint32_t* hard_fault_count) { | |
| 493 DCHECK(hard_fault_count); | |
| 494 | |
| 495 if (base::win::GetVersion() < base::win::VERSION_WIN7) | |
| 496 return false; | |
| 497 | |
| 498 // Get the function pointer. | |
| 499 static const NtQuerySystemInformationPtr query_sys_info = | |
| 500 reinterpret_cast<NtQuerySystemInformationPtr>(::GetProcAddress( | |
| 501 GetModuleHandle(L"ntdll.dll"), "NtQuerySystemInformation")); | |
| 502 if (query_sys_info == nullptr) | |
| 503 return false; | |
| 504 | |
| 505 // The output of this system call depends on the number of threads and | |
| 506 // processes on the entire system, and this can change between calls. Retry | |
| 507 // a small handful of times growing the buffer along the way. | |
| 508 // NOTE: The actual required size depends entirely on the number of processes | |
| 509 // and threads running on the system. The initial guess suffices for | |
| 510 // ~100s of processes and ~1000s of threads. | |
| 511 std::vector<uint8_t> buffer(32 * 1024); | |
| 512 for (size_t tries = 0; tries < 3; ++tries) { | |
| 513 ULONG return_length = 0; | |
| 514 NTSTATUS status = | |
| 515 query_sys_info(SystemProcessInformation, buffer.data(), | |
| 516 static_cast<ULONG>(buffer.size()), &return_length); | |
| 517 // Insufficient space in the buffer. | |
| 518 if (return_length > buffer.size()) { | |
| 519 buffer.resize(return_length); | |
| 520 continue; | |
| 521 } | |
| 522 if (NT_SUCCESS(status) && return_length <= buffer.size()) | |
| 523 break; | |
| 524 return false; | |
| 525 } | |
| 526 | |
| 527 // Look for the struct housing information for the current process. | |
| 528 DWORD proc_id = ::GetCurrentProcessId(); | |
| 529 size_t index = 0; | |
| 530 while (index < buffer.size()) { | |
| 531 DCHECK_LE(index + sizeof(SYSTEM_PROCESS_INFORMATION_EX), buffer.size()); | |
| 532 SYSTEM_PROCESS_INFORMATION_EX* proc_info = | |
| 533 reinterpret_cast<SYSTEM_PROCESS_INFORMATION_EX*>(buffer.data() + index); | |
| 534 if (base::win::HandleToUint32(proc_info->UniqueProcessId) == proc_id) { | |
| 535 *hard_fault_count = proc_info->HardFaultCount; | |
| 536 return true; | |
| 537 } | |
| 538 // The list ends when NextEntryOffset is zero. This also prevents busy | |
| 539 // looping if the data is in fact invalid. | |
| 540 if (proc_info->NextEntryOffset <= 0) | |
| 541 return false; | |
| 542 index += proc_info->NextEntryOffset; | |
| 543 } | |
| 544 | |
| 545 return false; | |
| 546 } | |
| 547 #endif // defined(OS_WIN) | |
| 548 | |
| 549 void RegisterPrefs(PrefRegistrySimple* registry) { | 566 void RegisterPrefs(PrefRegistrySimple* registry) { |
| 550 DCHECK(registry); | 567 DCHECK(registry); |
| 551 registry->RegisterInt64Pref(prefs::kLastStartupTimestamp, 0); | 568 registry->RegisterInt64Pref(prefs::kLastStartupTimestamp, 0); |
| 552 registry->RegisterStringPref(prefs::kLastStartupVersion, std::string()); | 569 registry->RegisterStringPref(prefs::kLastStartupVersion, std::string()); |
| 553 registry->RegisterIntegerPref(prefs::kSameVersionStartupCount, 0); | 570 registry->RegisterIntegerPref(prefs::kSameVersionStartupCount, 0); |
| 554 } | 571 } |
| 555 | 572 |
| 556 bool WasNonBrowserUIDisplayed() { | 573 bool WasNonBrowserUIDisplayed() { |
| 557 return g_non_browser_ui_displayed; | 574 return g_non_browser_ui_displayed; |
| 558 } | 575 } |
| (...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 764 UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT( | 781 UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT( |
| 765 UMA_HISTOGRAM_LONG_TIMES_100, | 782 UMA_HISTOGRAM_LONG_TIMES_100, |
| 766 "Startup.FirstWebContents.MainNavigationFinished", | 783 "Startup.FirstWebContents.MainNavigationFinished", |
| 767 g_process_creation_ticks.Get(), ticks); | 784 g_process_creation_ticks.Get(), ticks); |
| 768 } | 785 } |
| 769 | 786 |
| 770 base::TimeTicks MainEntryPointTicks() { | 787 base::TimeTicks MainEntryPointTicks() { |
| 771 return g_browser_main_entry_point_ticks.Get(); | 788 return g_browser_main_entry_point_ticks.Get(); |
| 772 } | 789 } |
| 773 | 790 |
| 774 StartupTemperature GetStartupTemperature() { | |
| 775 return g_startup_temperature; | |
| 776 } | |
| 777 | |
| 778 } // namespace startup_metric_utils | 791 } // namespace startup_metric_utils |
| OLD | NEW |