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 |