| Index: components/startup_metric_utils/startup_metric_utils.cc
|
| diff --git a/components/startup_metric_utils/startup_metric_utils.cc b/components/startup_metric_utils/startup_metric_utils.cc
|
| index 642a0c488823b675c2468a2098e0277227823212..b82da00fd0ccae419bfde47b2b76766cc6995ec9 100644
|
| --- a/components/startup_metric_utils/startup_metric_utils.cc
|
| +++ b/components/startup_metric_utils/startup_metric_utils.cc
|
| @@ -16,6 +16,11 @@
|
| #include "base/sys_info.h"
|
| #include "base/time/time.h"
|
|
|
| +#if defined(OS_WIN)
|
| +#include <winternl.h>
|
| +#include "base/win/windows_version.h"
|
| +#endif
|
| +
|
| namespace {
|
|
|
| // Mark as volatile to defensively make sure usage is thread-safe.
|
| @@ -40,6 +45,151 @@ base::Lock* GetSubsystemStartupTimeHashLock() {
|
| return slow_startup_time_hash_lock;
|
| }
|
|
|
| +#if defined(OS_WIN)
|
| +
|
| +// The struct used to return system process information via the NT internal
|
| +// QuerySystemInformation call. This is partially documented at
|
| +// http://goo.gl/Ja9MrH and fully documented at http://goo.gl/QJ70rn
|
| +// This structure is laid out in the same format on both 32-bit and 64-bit
|
| +// systems, but has a different size due to the various pointer-sized fields.
|
| +struct SYSTEM_PROCESS_INFORMATION_EX {
|
| + ULONG NextEntryOffset;
|
| + ULONG NumberOfThreads;
|
| + LARGE_INTEGER WorkingSetPrivateSize;
|
| + ULONG HardFaultCount;
|
| + BYTE Reserved1[36];
|
| + PVOID Reserved2[3];
|
| + // This is labeled a handle so that it expands to the correct size for 32-bit
|
| + // and 64-bit operating systems. However, under the hood it's a 32-bit DWORD
|
| + // containing the process ID.
|
| + HANDLE UniqueProcessId;
|
| + PVOID Reserved3;
|
| + ULONG HandleCount;
|
| + BYTE Reserved4[4];
|
| + PVOID Reserved5[11];
|
| + SIZE_T PeakPagefileUsage;
|
| + SIZE_T PrivatePageCount;
|
| + LARGE_INTEGER Reserved6[6];
|
| + // Array of SYSTEM_THREAD_INFORMATION structs follows.
|
| +};
|
| +
|
| +// The signature of the NtQuerySystemInformation function.
|
| +typedef NTSTATUS (WINAPI *NtQuerySystemInformationPtr)(
|
| + SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
|
| +
|
| +// Gets the hard fault count of the current process, returning it via
|
| +// |hard_fault_count|. Returns true on success, false otherwise. Also returns
|
| +// whether or not the system call was even possible for the current OS version
|
| +// via |has_os_support|.
|
| +bool GetHardFaultCountForCurrentProcess(uint32_t* hard_fault_count,
|
| + bool* has_os_support) {
|
| + DCHECK(hard_fault_count);
|
| + DCHECK(has_os_support);
|
| +
|
| + if (base::win::GetVersion() < base::win::VERSION_WIN7) {
|
| + *has_os_support = false;
|
| + return false;
|
| + }
|
| + // At this point the OS supports the required system call.
|
| + *has_os_support = true;
|
| +
|
| + // Get the function pointer.
|
| + NtQuerySystemInformationPtr query_sys_info =
|
| + reinterpret_cast<NtQuerySystemInformationPtr>(
|
| + ::GetProcAddress(GetModuleHandle(L"ntdll.dll"),
|
| + "NtQuerySystemInformation"));
|
| + if (query_sys_info == nullptr)
|
| + return false;
|
| +
|
| + // The output of this system call depends on the number of threads and
|
| + // processes on the entire system, and this can change between calls. Retry
|
| + // a small handful of times growing the buffer along the way.
|
| + // NOTE: The actual required size depends entirely on the number of processes
|
| + // and threads running on the system. The initial guess suffices for
|
| + // ~100s of processes and ~1000s of threads.
|
| + std::vector<uint8_t> buffer(32 * 1024);
|
| + for (size_t tries = 0; tries < 3; ++tries) {
|
| + ULONG return_length = 0;
|
| + NTSTATUS status = query_sys_info(
|
| + SystemProcessInformation,
|
| + buffer.data(),
|
| + static_cast<ULONG>(buffer.size()),
|
| + &return_length);
|
| + // Insufficient space in the buffer.
|
| + if (return_length > buffer.size()) {
|
| + buffer.resize(return_length);
|
| + continue;
|
| + }
|
| + if (NT_SUCCESS(status) && return_length <= buffer.size())
|
| + break;
|
| + return false;
|
| + }
|
| +
|
| + // Look for the struct housing information for the current process.
|
| + DWORD proc_id = ::GetCurrentProcessId();
|
| + size_t index = 0;
|
| + while (index < buffer.size()) {
|
| + DCHECK_LE(index + sizeof(SYSTEM_PROCESS_INFORMATION_EX), buffer.size());
|
| + SYSTEM_PROCESS_INFORMATION_EX* proc_info =
|
| + reinterpret_cast<SYSTEM_PROCESS_INFORMATION_EX*>(buffer.data() + index);
|
| + if (reinterpret_cast<DWORD>(proc_info->UniqueProcessId) == proc_id) {
|
| + *hard_fault_count = proc_info->HardFaultCount;
|
| + return true;
|
| + }
|
| + // The list ends when NextEntryOffset is zero. This also prevents busy
|
| + // looping if the data is in fact invalid.
|
| + if (proc_info->NextEntryOffset <= 0)
|
| + return false;
|
| + index += proc_info->NextEntryOffset;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +#endif // defined(OS_WIN)
|
| +
|
| +// On Windows, records the number of hard-faults that have occurred in the
|
| +// current chrome.exe process since it was started. This is a nop on other
|
| +// platforms.
|
| +// crbug.com/476923
|
| +// TODO(chrisha): If this proves useful, use it to split startup stats in two.
|
| +void RecordHardFaultHistogram(bool is_first_run) {
|
| +#if defined(OS_WIN)
|
| + uint32_t hard_fault_count = 0;
|
| + bool has_os_support = false;
|
| + bool success = GetHardFaultCountForCurrentProcess(
|
| + &hard_fault_count, &has_os_support);
|
| +
|
| + // Log whether or not the system call was successful, assuming the OS was
|
| + // detected to support it.
|
| + if (has_os_support) {
|
| + UMA_HISTOGRAM_BOOLEAN(
|
| + "Startup.BrowserMessageLoopStartHardFaultCount.Success",
|
| + success);
|
| + }
|
| +
|
| + // Don't log a histogram value if unable to get the hard fault count.
|
| + if (!success)
|
| + return;
|
| +
|
| + // Hard fault counts are expected to be in the thousands range,
|
| + // corresponding to faulting in ~10s of MBs of code ~10s of KBs at a time.
|
| + // (Observed to vary from 1000 to 10000 on various test machines and
|
| + // platforms.)
|
| + if (is_first_run) {
|
| + UMA_HISTOGRAM_CUSTOM_COUNTS(
|
| + "Startup.BrowserMessageLoopStartHardFaultCount.FirstRun",
|
| + hard_fault_count,
|
| + 0, 40000, 50);
|
| + } else {
|
| + UMA_HISTOGRAM_CUSTOM_COUNTS(
|
| + "Startup.BrowserMessageLoopStartHardFaultCount",
|
| + hard_fault_count,
|
| + 0, 40000, 50);
|
| + }
|
| +#endif // defined(OS_WIN)
|
| +}
|
| +
|
| // Record time of main entry so it can be read from Telemetry performance
|
| // tests.
|
| // TODO(jeremy): Remove once crbug.com/317481 is fixed.
|
| @@ -115,6 +265,7 @@ const base::Time MainEntryStartTime() {
|
| }
|
|
|
| void OnBrowserStartupComplete(bool is_first_run) {
|
| + RecordHardFaultHistogram(is_first_run);
|
| RecordMainEntryTimeHistogram();
|
|
|
| // Bail if uptime < 7 minutes, to filter out cases where Chrome may have been
|
|
|