Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(197)

Side by Side Diff: components/startup_metric_utils/startup_metric_utils.cc

Issue 1084943002: Add UMA stats for hard-fault counts for Windows 7 and greater. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed comments from Erik's review. Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tools/metrics/histograms/histograms.xml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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/startup_metric_utils.h" 5 #include "components/startup_metric_utils/startup_metric_utils.h"
6 6
7 #include "base/containers/hash_tables.h" 7 #include "base/containers/hash_tables.h"
8 #include "base/environment.h" 8 #include "base/environment.h"
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/metrics/histogram.h" 10 #include "base/metrics/histogram.h"
11 #include "base/metrics/histogram_base.h" 11 #include "base/metrics/histogram_base.h"
12 #include "base/metrics/statistics_recorder.h" 12 #include "base/metrics/statistics_recorder.h"
13 #include "base/process/process_info.h" 13 #include "base/process/process_info.h"
14 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/string_number_conversions.h"
15 #include "base/synchronization/lock.h" 15 #include "base/synchronization/lock.h"
16 #include "base/sys_info.h" 16 #include "base/sys_info.h"
17 #include "base/time/time.h" 17 #include "base/time/time.h"
18 18
19 #if defined(OS_WIN)
20 #include <winternl.h>
21 #endif
22
19 namespace { 23 namespace {
20 24
21 // Mark as volatile to defensively make sure usage is thread-safe. 25 // Mark as volatile to defensively make sure usage is thread-safe.
22 // Note that at the time of this writing, access is only on the UI thread. 26 // Note that at the time of this writing, access is only on the UI thread.
23 volatile bool g_non_browser_ui_displayed = false; 27 volatile bool g_non_browser_ui_displayed = false;
24 28
25 base::Time* MainEntryPointTimeInternal() { 29 base::Time* MainEntryPointTimeInternal() {
26 static base::Time main_start_time = base::Time::Now(); 30 static base::Time main_start_time = base::Time::Now();
27 return &main_start_time; 31 return &main_start_time;
28 } 32 }
29 33
30 typedef base::hash_map<std::string,base::TimeDelta> SubsystemStartupTimeHash; 34 typedef base::hash_map<std::string,base::TimeDelta> SubsystemStartupTimeHash;
31 35
32 SubsystemStartupTimeHash* GetSubsystemStartupTimeHash() { 36 SubsystemStartupTimeHash* GetSubsystemStartupTimeHash() {
33 static SubsystemStartupTimeHash* slow_startup_time_hash = 37 static SubsystemStartupTimeHash* slow_startup_time_hash =
34 new SubsystemStartupTimeHash; 38 new SubsystemStartupTimeHash;
35 return slow_startup_time_hash; 39 return slow_startup_time_hash;
36 } 40 }
37 41
38 base::Lock* GetSubsystemStartupTimeHashLock() { 42 base::Lock* GetSubsystemStartupTimeHashLock() {
39 static base::Lock* slow_startup_time_hash_lock = new base::Lock; 43 static base::Lock* slow_startup_time_hash_lock = new base::Lock;
40 return slow_startup_time_hash_lock; 44 return slow_startup_time_hash_lock;
41 } 45 }
42 46
47 #if defined(OS_WIN)
48
49 // The struct used to return system process information via the NT internal
50 // QuerySystemInformation call. This is partially documented at
51 // http://goo.gl/Ja9MrH and fully documented at http://goo.gl/QJ70rn
52 // This structure is laid out in the same format on both 32-bit and 64-bit
53 // systems, but has a different size due to the various pointer-sized fields.
54 struct SYSTEM_PROCESS_INFORMATION_EX {
55 ULONG NextEntryOffset;
56 ULONG NumberOfThreads;
57 LARGE_INTEGER WorkingSetPrivateSize;
58 ULONG HardFaultCount;
59 BYTE Reserved1[36];
60 PVOID Reserved2[3];
61 // This is labeled a handle so that it expands to the correct size for 32-bit
62 // and 64-bit operating systems. However, under the hood it's a 32-bit DWORD
63 // containing the process ID.
64 HANDLE UniqueProcessId;
65 PVOID Reserved3;
66 ULONG HandleCount;
67 BYTE Reserved4[4];
68 PVOID Reserved5[11];
69 SIZE_T PeakPagefileUsage;
70 SIZE_T PrivatePageCount;
71 LARGE_INTEGER Reserved6[6];
72 // Array of SYSTEM_THREAD_INFORMATION structs follows.
73 };
74
75 // The signature of the NtQuerySystemInformation function.
76 typedef NTSTATUS (WINAPI *NtQuerySystemInformationPtr)(
77 SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
78
79 // Gets the hard fault count of the current process, returning it via
80 // |hard_fault_count|. Returns true on success, false otherwise. Also returns
81 // whether or not the system call was even possible for the current OS version
82 // via |has_os_support|.
83 bool GetHardFaultCountForCurrentProcess(uint32* hard_fault_count,
Alexei Svitkine (slow) 2015/04/15 17:36:19 Nit: uint32_t is best practice for new code.
chrisha 2015/04/15 17:56:01 Done.
84 bool* has_os_support) {
85 DCHECK(hard_fault_count);
86 DCHECK(has_os_support);
87
88 *has_os_support = false;
89 OSVERSIONINFOEX version_info = {};
90 version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
91 if (!::GetVersionEx(reinterpret_cast<LPOSVERSIONINFOW>(&version_info)))
Alexei Svitkine (slow) 2015/04/15 17:36:19 Can you use base/win/windows_version.h instead of
chrisha 2015/04/15 17:56:01 I certainly can, and it's far simpler that way. Th
92 return false;
93
94 // The hard fault counter is only available as of Windows 7 (version 6.1).
95 if (version_info.dwMajorVersion < 6)
96 return false;
97 if (version_info.dwMajorVersion == 6 && version_info.dwMinorVersion < 1)
98 return false;
99
100 // At this point the OS supports the required system call.
101 *has_os_support = true;
102
103 // Get the function pointer.
104 NtQuerySystemInformationPtr query_sys_info =
105 reinterpret_cast<NtQuerySystemInformationPtr>(
106 ::GetProcAddress(GetModuleHandle(L"ntdll.dll"),
107 "NtQuerySystemInformation"));
108 if (query_sys_info == nullptr)
109 return false;
110
111 // The output of this system call depends on the number of threads and
112 // processes on the entire system, and this can change between calls. Retry
113 // a small handful of times growing the buffer along the way.
114 // NOTE: The actual required size depends entirely on the number of processes
115 // and threads running on the system. The initial guess suffices for
116 // ~100s of processes and ~1000s of threads.
117 std::vector<uint8> buffer(32 * 1024);
118 for (size_t tries = 0; tries < 3; ++tries) {
119 ULONG return_length = 0;
120 NTSTATUS status = query_sys_info(
121 SystemProcessInformation,
122 buffer.data(),
123 static_cast<ULONG>(buffer.size()),
124 &return_length);
125 // Insufficient space in the buffer.
126 if (return_length > buffer.size()) {
127 buffer.resize(return_length);
128 continue;
129 }
130 if (status == 0 && return_length <= buffer.size())
Alexei Svitkine (slow) 2015/04/15 17:36:19 Should use NT_SUCCESS() instead of status == 0 I t
chrisha 2015/04/15 17:56:01 Done.
131 break;
132 return false;
133 }
134
135 // Look for the struct housing information for the current process.
136 DWORD proc_id = ::GetCurrentProcessId();
137 size_t index = 0;
138 while (index < buffer.size()) {
139 SYSTEM_PROCESS_INFORMATION_EX* proc_info =
140 reinterpret_cast<SYSTEM_PROCESS_INFORMATION_EX*>(buffer.data() + index);
141 if (reinterpret_cast<DWORD>(proc_info->UniqueProcessId) == proc_id) {
142 *hard_fault_count = proc_info->HardFaultCount;
143 return true;
144 }
145 // The list ends with NextEntryOffset is zero. This also prevents busy
146 // looping if the data is in fact invalid.
147 if (proc_info->NextEntryOffset == 0)
148 return false;
149 index += proc_info->NextEntryOffset;
150 }
151
152 return false;
153 }
154
155 #endif // defined(OS_WIN)
156
157 // On Windows, records the number of hard-faults that have occurred in the
158 // current chrome.exe process since it was started. This is a nop on other
159 // platforms.
160 // crbug.com/476923
161 // TODO(chrisha): If this proves useful, use it to split startup stats in two.
162 void RecordHardFaultHistogram(bool is_first_run) {
163 #if defined(OS_WIN)
164 uint32 hard_fault_count = 0;
165 bool has_os_support = false;
166 bool success = GetHardFaultCountForCurrentProcess(
167 &hard_fault_count, &has_os_support);
168
169 // Log whether or not the system call was successful, assuming the OS was
170 // detected to support it.
171 if (has_os_support) {
172 UMA_HISTOGRAM_BOOLEAN(
173 "Startup.BrowserMessageLoopStartHardFaultCount.Success",
174 success);
Alexei Svitkine (slow) 2015/04/15 17:36:19 Since there are different cases available, I sugge
chrisha 2015/04/15 17:56:01 The "no OS support" case is completely determinist
Alexei Svitkine (slow) 2015/04/15 18:08:10 I was thinking it would be simpler to reason about
175 }
176
177 // Don't log a histogram value if unable to get the hard fault count.
178 if (!success)
179 return;
180
181 // Hard fault counts are expected to be in the thousands range,
182 // corresponding to faulting in ~10s of MBs of code ~10s of KBs at a time.
183 // (Observed to vary from 1000 to 10000 on various test machines and
184 // platforms.)
185 if (is_first_run) {
186 UMA_HISTOGRAM_CUSTOM_COUNTS(
187 "Startup.BrowserMessageLoopStartHardFaultCount.FirstRun",
188 hard_fault_count,
189 0, 40000, 50);
190 } else {
191 UMA_HISTOGRAM_CUSTOM_COUNTS(
192 "Startup.BrowserMessageLoopStartHardFaultCount",
193 hard_fault_count,
194 0, 40000, 50);
195 }
196 #endif // defined(OS_WIN)
197 }
198
43 // Record time of main entry so it can be read from Telemetry performance 199 // Record time of main entry so it can be read from Telemetry performance
44 // tests. 200 // tests.
45 // TODO(jeremy): Remove once crbug.com/317481 is fixed. 201 // TODO(jeremy): Remove once crbug.com/317481 is fixed.
46 void RecordMainEntryTimeHistogram() { 202 void RecordMainEntryTimeHistogram() {
47 const int kLowWordMask = 0xFFFFFFFF; 203 const int kLowWordMask = 0xFFFFFFFF;
48 const int kLower31BitsMask = 0x7FFFFFFF; 204 const int kLower31BitsMask = 0x7FFFFFFF;
49 base::TimeDelta browser_main_entry_time_absolute = 205 base::TimeDelta browser_main_entry_time_absolute =
50 *MainEntryPointTimeInternal() - base::Time::UnixEpoch(); 206 *MainEntryPointTimeInternal() - base::Time::UnixEpoch();
51 207
52 uint64 browser_main_entry_time_raw_ms = 208 uint64 browser_main_entry_time_raw_ms =
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
108 } 264 }
109 #endif // OS_ANDROID 265 #endif // OS_ANDROID
110 266
111 // Return the time recorded by RecordMainEntryPointTime(). 267 // Return the time recorded by RecordMainEntryPointTime().
112 const base::Time MainEntryStartTime() { 268 const base::Time MainEntryStartTime() {
113 DCHECK(g_main_entry_time_was_recorded); 269 DCHECK(g_main_entry_time_was_recorded);
114 return *MainEntryPointTimeInternal(); 270 return *MainEntryPointTimeInternal();
115 } 271 }
116 272
117 void OnBrowserStartupComplete(bool is_first_run) { 273 void OnBrowserStartupComplete(bool is_first_run) {
274 RecordHardFaultHistogram(is_first_run);
118 RecordMainEntryTimeHistogram(); 275 RecordMainEntryTimeHistogram();
119 276
120 // Bail if uptime < 7 minutes, to filter out cases where Chrome may have been 277 // Bail if uptime < 7 minutes, to filter out cases where Chrome may have been
121 // autostarted and the machine is under io pressure. 278 // autostarted and the machine is under io pressure.
122 const int64 kSevenMinutesInMilliseconds = 279 const int64 kSevenMinutesInMilliseconds =
123 base::TimeDelta::FromMinutes(7).InMilliseconds(); 280 base::TimeDelta::FromMinutes(7).InMilliseconds();
124 if (base::SysInfo::Uptime() < kSevenMinutesInMilliseconds) { 281 if (base::SysInfo::Uptime() < kSevenMinutesInMilliseconds) {
125 g_startup_stats_collection_finished = true; 282 g_startup_stats_collection_finished = true;
126 return; 283 return;
127 } 284 }
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
242 SubsystemStartupTimeHash* hash = GetSubsystemStartupTimeHash(); 399 SubsystemStartupTimeHash* hash = GetSubsystemStartupTimeHash();
243 // Only record the initial sample for a given histogram. 400 // Only record the initial sample for a given histogram.
244 if (hash->find(histogram_name_) != hash->end()) 401 if (hash->find(histogram_name_) != hash->end())
245 return; 402 return;
246 403
247 (*hash)[histogram_name_] = 404 (*hash)[histogram_name_] =
248 base::TimeTicks::Now() - start_time_; 405 base::TimeTicks::Now() - start_time_;
249 } 406 }
250 407
251 } // namespace startup_metric_utils 408 } // namespace startup_metric_utils
OLDNEW
« no previous file with comments | « no previous file | tools/metrics/histograms/histograms.xml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698