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..d76cc0e858a78804b9abbb396f8e7ef26820f21e 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,156 @@ 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* 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))) |
+ 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 (reinterpret_cast<DWORD>(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; |
+} |
+ |
+#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 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 |
erikchen
2015/04/14 23:01:52
If you're observing 10000 on a test machine, it's
chrisha
2015/04/15 13:04:30
I suppose it could go drastically higher, but it w
erikchen
2015/04/15 17:26:59
Sounds reasonable to me. We can always change the
|
+ // 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", |
erikchen
2015/04/14 23:01:52
The description you give for this metric implies i
chrisha
2015/04/15 13:04:29
I was just being consistent with how Startup.Brows
|
+ hard_fault_count, |
+ 0, 20000, 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 +269,7 @@ const base::Time MainEntryStartTime() { |
} |
void OnBrowserStartupComplete(bool is_first_run) { |
+ RecordHardFaultHistogram(is_first_run); |
erikchen
2015/04/14 23:01:52
As a quick sanity check, can you confirm that this
chrisha
2015/04/15 13:04:30
This was benchmarked across various versions of th
|
RecordMainEntryTimeHistogram(); |
// Bail if uptime < 7 minutes, to filter out cases where Chrome may have been |