Chromium Code Reviews| 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..dd108fba2f132f2ebadeb04cbf9002ca1e299a0a 100644 |
| --- a/components/startup_metric_utils/startup_metric_utils.cc |
| +++ b/components/startup_metric_utils/startup_metric_utils.cc |
| @@ -16,6 +16,10 @@ |
| #include "base/sys_info.h" |
| #include "base/time/time.h" |
| +#if defined(OS_WIN) |
| +#include <winternl.h> |
| +#endif |
| + |
| namespace { |
| // Mark as volatile to defensively make sure usage is thread-safe. |
| @@ -40,6 +44,148 @@ base::Lock* GetSubsystemStartupTimeHashLock() { |
| return slow_startup_time_hash_lock; |
| } |
| +// 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 |
| +struct SYSTEM_PROCESS_INFORMATION_EX { |
| + ULONG NextEntryOffset; |
| + ULONG NumberOfThreads; |
| + LARGE_INTEGER WorkingSetPrivateSize; |
| + ULONG HardFaultCount; |
| + BYTE Reserved1[36]; |
| + PVOID Reserved2[3]; |
| + ULONG 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. |
| +}; |
| +static_assert( |
| + sizeof(SYSTEM_PROCESS_INFORMATION_EX) == 184, |
|
Alexei Svitkine (slow)
2015/04/14 16:53:51
Is the size the same between 32 and 64 bit?
chrisha
2015/04/14 17:09:07
Good catch. Not the same size, but has the same la
|
| + "The definition of SYSTEM_PROCESS_INFORMATION_EX does not match the " |
| + "expected size."); |
| + |
| +// 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* hard_fault_count, |
| + bool* has_os_support) { |
| + DCHECK(hard_fault_count); |
| + DCHECK(has_os_support); |
| + |
| + *has_os_support = false; |
| + OSVERSIONINFOEX version_info = {}; |
| + version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); |
| + if (!::GetVersionEx(reinterpret_cast<LPOSVERSIONINFOW>(&version_info))) |
|
Alexei Svitkine (slow)
2015/04/14 16:53:51
Is this inside a file that's Windows-specific? If
|
| + return false; |
| + |
| + // The hard fault counter is only available as of Windows 7 (version 6.1). |
| + if (version_info.dwMajorVersion < 6) |
| + return false; |
| + if (version_info.dwMajorVersion == 6 && version_info.dwMinorVersion < 1) |
| + 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. |
| + std::vector<uint8> buffer(1024); |
| + for (size_t tries = 0; tries < 3; ++tries) { |
| + ULONG return_length = 0; |
| + NTSTATUS status = query_sys_info( |
| + SystemProcessInformation, |
| + buffer.data(), |
| + buffer.size(), |
| + &return_length); |
| + // Insufficient space in the buffer. |
| + if (return_length > buffer.size()) { |
| + buffer.resize(return_length); |
| + continue; |
| + } |
| + if (status == 0 && return_length <= buffer.size()) |
| + break; |
| + return false; |
| + } |
| + // The only way out to get here is if the system call was successful. |
| + |
| + // Look for the struct housing information for the current process. |
| + DWORD proc_id = ::GetCurrentProcessId(); |
| + size_t index = 0; |
| + while (index < buffer.size()) { |
| + SYSTEM_PROCESS_INFORMATION_EX* proc_info = |
| + reinterpret_cast<SYSTEM_PROCESS_INFORMATION_EX*>(buffer.data() + index); |
| + if (proc_info->UniqueProcessId == proc_id) { |
| + *hard_fault_count = proc_info->HardFaultCount; |
| + return true; |
| + } |
| + // The list ends with 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; |
| +} |
| + |
| +// On Windows, records the number of hard-faults that have occurred in the |
| +// current chrome.exe process since it was started. |
| +// crbug.com/476923 |
| +// TODO(chrisha): If this proves useful, use it to split startup stats in two. |
| +void RecordHardFaultHistogram(bool is_first_run) { |
| + uint32 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, 20000, 50); |
| + } else { |
| + UMA_HISTOGRAM_CUSTOM_COUNTS( |
| + "Startup.BrowserMessageLoopStartHardFaultCount", |
| + hard_fault_count, |
| + 0, 20000, 50); |
| + } |
| +} |
| + |
| // 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 +261,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 |