| OLD | NEW |
| 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/lazy_instance.h" | 9 #include "base/lazy_instance.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/metrics/histogram_macros.h" | 11 #include "base/metrics/histogram_macros.h" |
| 12 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
| 13 #include "base/sys_info.h" | 13 #include "base/sys_info.h" |
| 14 #include "base/trace_event/trace_event.h" |
| 14 | 15 |
| 15 #if defined(OS_WIN) | 16 #if defined(OS_WIN) |
| 16 #include <winternl.h> | 17 #include <winternl.h> |
| 17 #include "base/win/windows_version.h" | 18 #include "base/win/windows_version.h" |
| 18 #endif | 19 #endif |
| 19 | 20 |
| 20 namespace startup_metric_utils { | 21 namespace startup_metric_utils { |
| 21 | 22 |
| 22 namespace { | 23 namespace { |
| 23 | 24 |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 155 // Helper macro for splitting out an UMA histogram based on cold or warm start. | 156 // Helper macro for splitting out an UMA histogram based on cold or warm start. |
| 156 // |type| is the histogram type, and corresponds to an UMA macro like | 157 // |type| is the histogram type, and corresponds to an UMA macro like |
| 157 // UMA_HISTOGRAM_LONG_TIMES. It must be itself be a macro that only takes two | 158 // UMA_HISTOGRAM_LONG_TIMES. It must be itself be a macro that only takes two |
| 158 // parameters. | 159 // parameters. |
| 159 // |basename| is the basename of the histogram. A histogram of this name will | 160 // |basename| is the basename of the histogram. A histogram of this name will |
| 160 // always be recorded to. If the startup is either cold or warm then a value | 161 // always be recorded to. If the startup is either cold or warm then a value |
| 161 // will also be recorded to the histogram with name |basename| and suffix | 162 // will also be recorded to the histogram with name |basename| and suffix |
| 162 // ".ColdStart" or ".WarmStart", as appropriate. | 163 // ".ColdStart" or ".WarmStart", as appropriate. |
| 163 // |value_expr| is an expression evaluating to the value to be recorded. This | 164 // |value_expr| is an expression evaluating to the value to be recorded. This |
| 164 // will be evaluated exactly once and cached, so side effects are not an issue. | 165 // will be evaluated exactly once and cached, so side effects are not an issue. |
| 165 #define UMA_HISTOGRAM_BY_STARTUP_TEMPERATURE(type, basename, value_expr) { \ | 166 #define UMA_HISTOGRAM_WITH_STARTUP_TEMPERATURE(type, basename, value_expr) \ |
| 166 const auto kValue = value_expr; \ | 167 { \ |
| 167 /* Always record to the base histogram. */ \ | 168 const auto kValue = value_expr; \ |
| 168 type(basename, kValue); \ | 169 /* Always record to the base histogram. */ \ |
| 169 /* Record to the cold/warm suffixed histogram as appropriate. */ \ | 170 type(basename, kValue); \ |
| 170 if (g_startup_temperature == COLD_STARTUP_TEMPERATURE) { \ | 171 /* Record to the cold/warm suffixed histogram as appropriate. */ \ |
| 171 type(basename ".ColdStartup", kValue); \ | 172 if (g_startup_temperature == COLD_STARTUP_TEMPERATURE) { \ |
| 172 } else if (g_startup_temperature == WARM_STARTUP_TEMPERATURE) { \ | 173 type(basename ".ColdStartup", kValue); \ |
| 173 type(basename ".WarmStartup", kValue); \ | 174 } else if (g_startup_temperature == WARM_STARTUP_TEMPERATURE) { \ |
| 174 } \ | 175 type(basename ".WarmStartup", kValue); \ |
| 175 } | 176 } \ |
| 177 } |
| 176 | 178 |
| 179 #define UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE(type, basename, \ |
| 180 begin_time, end_time) \ |
| 181 { \ |
| 182 UMA_HISTOGRAM_WITH_STARTUP_TEMPERATURE(type, basename, \ |
| 183 end_time - begin_time) \ |
| 184 TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP1( \ |
| 185 "startup", basename, 0, \ |
| 186 StartupTimeToTraceTicks(begin_time).ToInternalValue(), "Temperature", \ |
| 187 g_startup_temperature); \ |
| 188 TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP1( \ |
| 189 "startup", basename, 0, \ |
| 190 StartupTimeToTraceTicks(end_time).ToInternalValue(), "Temperature", \ |
| 191 g_startup_temperature); \ |
| 192 } |
| 177 | 193 |
| 178 // On Windows, records the number of hard-faults that have occurred in the | 194 // On Windows, records the number of hard-faults that have occurred in the |
| 179 // current chrome.exe process since it was started. This is a nop on other | 195 // current chrome.exe process since it was started. This is a nop on other |
| 180 // platforms. | 196 // platforms. |
| 181 // crbug.com/476923 | 197 // crbug.com/476923 |
| 182 // TODO(chrisha): If this proves useful, use it to split startup stats in two. | 198 // TODO(chrisha): If this proves useful, use it to split startup stats in two. |
| 183 void RecordHardFaultHistogram(bool is_first_run) { | 199 void RecordHardFaultHistogram(bool is_first_run) { |
| 184 #if defined(OS_WIN) | 200 #if defined(OS_WIN) |
| 185 uint32_t hard_fault_count = 0; | 201 uint32_t hard_fault_count = 0; |
| 186 bool has_os_support = false; | 202 bool has_os_support = false; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 222 } else if (hard_fault_count >= COLD_START_HARD_FAULT_COUNT_THRESHOLD) { | 238 } else if (hard_fault_count >= COLD_START_HARD_FAULT_COUNT_THRESHOLD) { |
| 223 g_startup_temperature = COLD_STARTUP_TEMPERATURE; | 239 g_startup_temperature = COLD_STARTUP_TEMPERATURE; |
| 224 } | 240 } |
| 225 | 241 |
| 226 // Record the startup 'temperature'. | 242 // Record the startup 'temperature'. |
| 227 UMA_HISTOGRAM_ENUMERATION( | 243 UMA_HISTOGRAM_ENUMERATION( |
| 228 "Startup.Temperature", g_startup_temperature, STARTUP_TEMPERATURE_MAX); | 244 "Startup.Temperature", g_startup_temperature, STARTUP_TEMPERATURE_MAX); |
| 229 #endif // defined(OS_WIN) | 245 #endif // defined(OS_WIN) |
| 230 } | 246 } |
| 231 | 247 |
| 248 // Converts a base::Time value to a base::TraceTicks value. The conversion isn't |
| 249 // exact, but is within the time delta taken to synchronously resolve |
| 250 // base::Time::Now() and base::TraceTicks::Now() which in practice is pretty |
| 251 // much instant compared to multi-seconds startup timings. |
| 252 // TODO(gab): Find a precise way to do this (http://crbug.com/544131). |
| 253 base::TraceTicks StartupTimeToTraceTicks(const base::Time& time) { |
| 254 // First get a base which represents the same point in time in both units. |
| 255 // The wall clock time it takes to gather both of these is the precision of |
| 256 // this method. |
| 257 static const base::Time time_base = base::Time::Now(); |
| 258 static const base::TraceTicks trace_ticks_base = base::TraceTicks::Now(); |
| 259 |
| 260 // Then use the TimeDelta common ground between the two units to make the |
| 261 // conversion. |
| 262 const base::TimeDelta delta_since_base = time_base - time; |
| 263 return trace_ticks_base - delta_since_base; |
| 264 } |
| 265 |
| 232 // Record time of main entry so it can be read from Telemetry performance tests. | 266 // Record time of main entry so it can be read from Telemetry performance tests. |
| 233 // TODO(jeremy): Remove once crbug.com/317481 is fixed. | 267 // TODO(jeremy): Remove once crbug.com/317481 is fixed. |
| 234 void RecordMainEntryTimeHistogram() { | 268 void RecordMainEntryTimeHistogram() { |
| 235 const int kLowWordMask = 0xFFFFFFFF; | 269 const int kLowWordMask = 0xFFFFFFFF; |
| 236 const int kLower31BitsMask = 0x7FFFFFFF; | 270 const int kLower31BitsMask = 0x7FFFFFFF; |
| 237 DCHECK(!MainEntryPointTime().is_null()); | 271 DCHECK(!MainEntryPointTime().is_null()); |
| 238 base::TimeDelta browser_main_entry_time_absolute = | 272 base::TimeDelta browser_main_entry_time_absolute = |
| 239 MainEntryPointTime() - base::Time::UnixEpoch(); | 273 MainEntryPointTime() - base::Time::UnixEpoch(); |
| 240 | 274 |
| 241 uint64 browser_main_entry_time_raw_ms = | 275 uint64 browser_main_entry_time_raw_ms = |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 300 env->SetVar(kChromeMainTimeEnvVar, exe_load_time); | 334 env->SetVar(kChromeMainTimeEnvVar, exe_load_time); |
| 301 } | 335 } |
| 302 | 336 |
| 303 void RecordBrowserMainMessageLoopStart(const base::Time& time, | 337 void RecordBrowserMainMessageLoopStart(const base::Time& time, |
| 304 bool is_first_run) { | 338 bool is_first_run) { |
| 305 RecordHardFaultHistogram(is_first_run); | 339 RecordHardFaultHistogram(is_first_run); |
| 306 RecordMainEntryTimeHistogram(); | 340 RecordMainEntryTimeHistogram(); |
| 307 | 341 |
| 308 const base::Time& process_creation_time = g_process_creation_time.Get(); | 342 const base::Time& process_creation_time = g_process_creation_time.Get(); |
| 309 if (!is_first_run && !process_creation_time.is_null()) { | 343 if (!is_first_run && !process_creation_time.is_null()) { |
| 310 UMA_HISTOGRAM_BY_STARTUP_TEMPERATURE( | 344 UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE( |
| 311 UMA_HISTOGRAM_LONG_TIMES_100, | 345 UMA_HISTOGRAM_LONG_TIMES_100, "Startup.BrowserMessageLoopStartTime", |
| 312 "Startup.BrowserMessageLoopStartTime", | 346 process_creation_time, time); |
| 313 time - process_creation_time); | |
| 314 } | 347 } |
| 315 | 348 |
| 316 // Bail if uptime < 7 minutes, to filter out cases where Chrome may have been | 349 // Bail if uptime < 7 minutes, to filter out cases where Chrome may have been |
| 317 // autostarted and the machine is under io pressure. | 350 // autostarted and the machine is under io pressure. |
| 318 const int64 kSevenMinutesInMilliseconds = | 351 const int64 kSevenMinutesInMilliseconds = |
| 319 base::TimeDelta::FromMinutes(7).InMilliseconds(); | 352 base::TimeDelta::FromMinutes(7).InMilliseconds(); |
| 320 if (base::SysInfo::Uptime() < kSevenMinutesInMilliseconds) | 353 if (base::SysInfo::Uptime() < kSevenMinutesInMilliseconds) |
| 321 return; | 354 return; |
| 322 | 355 |
| 323 // The Startup.BrowserMessageLoopStartTime histogram exhibits instability in | 356 // The Startup.BrowserMessageLoopStartTime histogram exhibits instability in |
| 324 // the field which limits its usefulness in all scenarios except when we have | 357 // the field which limits its usefulness in all scenarios except when we have |
| 325 // a very large sample size. Attempt to mitigate this with a new metric: | 358 // a very large sample size. Attempt to mitigate this with a new metric: |
| 326 // * Measure time from main entry rather than the OS' notion of process start. | 359 // * Measure time from main entry rather than the OS' notion of process start. |
| 327 // * Only measure launches that occur 7 minutes after boot to try to avoid | 360 // * Only measure launches that occur 7 minutes after boot to try to avoid |
| 328 // cases where Chrome is auto-started and IO is heavily loaded. | 361 // cases where Chrome is auto-started and IO is heavily loaded. |
| 329 const base::Time dll_main_time = MainEntryPointTime(); | 362 const base::Time dll_main_time = MainEntryPointTime(); |
| 330 base::TimeDelta startup_time_from_main_entry = time - dll_main_time; | |
| 331 if (is_first_run) { | 363 if (is_first_run) { |
| 332 UMA_HISTOGRAM_BY_STARTUP_TEMPERATURE( | 364 UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE( |
| 333 UMA_HISTOGRAM_LONG_TIMES, | 365 UMA_HISTOGRAM_LONG_TIMES, |
| 334 "Startup.BrowserMessageLoopStartTimeFromMainEntry.FirstRun", | 366 "Startup.BrowserMessageLoopStartTimeFromMainEntry.FirstRun", |
| 335 startup_time_from_main_entry); | 367 dll_main_time, time); |
| 336 } else { | 368 } else { |
| 337 UMA_HISTOGRAM_BY_STARTUP_TEMPERATURE( | 369 UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE( |
| 338 UMA_HISTOGRAM_LONG_TIMES, | 370 UMA_HISTOGRAM_LONG_TIMES, |
| 339 "Startup.BrowserMessageLoopStartTimeFromMainEntry", | 371 "Startup.BrowserMessageLoopStartTimeFromMainEntry", dll_main_time, |
| 340 startup_time_from_main_entry); | 372 time); |
| 341 } | 373 } |
| 342 | 374 |
| 343 // Record timings between process creation, the main() in the executable being | 375 // Record timings between process creation, the main() in the executable being |
| 344 // reached and the main() in the shared library being reached. | 376 // reached and the main() in the shared library being reached. |
| 345 if (!process_creation_time.is_null()) { | 377 if (!process_creation_time.is_null()) { |
| 346 const base::Time exe_main_time = ExeMainEntryPointTime(); | 378 const base::Time exe_main_time = ExeMainEntryPointTime(); |
| 347 if (!exe_main_time.is_null()) { | 379 if (!exe_main_time.is_null()) { |
| 348 // Process create to chrome.exe:main(). | 380 // Process create to chrome.exe:main(). |
| 349 UMA_HISTOGRAM_BY_STARTUP_TEMPERATURE( | 381 UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE( |
| 350 UMA_HISTOGRAM_LONG_TIMES, "Startup.LoadTime.ProcessCreateToExeMain", | 382 UMA_HISTOGRAM_LONG_TIMES, "Startup.LoadTime.ProcessCreateToExeMain", |
| 351 exe_main_time - process_creation_time); | 383 process_creation_time, exe_main_time); |
| 352 | 384 |
| 353 // chrome.exe:main() to chrome.dll:main(). | 385 // chrome.exe:main() to chrome.dll:main(). |
| 354 UMA_HISTOGRAM_BY_STARTUP_TEMPERATURE( | 386 UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE( |
| 355 UMA_HISTOGRAM_LONG_TIMES, "Startup.LoadTime.ExeMainToDllMain", | 387 UMA_HISTOGRAM_LONG_TIMES, "Startup.LoadTime.ExeMainToDllMain", |
| 356 dll_main_time - exe_main_time); | 388 exe_main_time, dll_main_time); |
| 357 | 389 |
| 358 // Process create to chrome.dll:main(). | 390 // Process create to chrome.dll:main(). Reported as a histogram only as |
| 359 UMA_HISTOGRAM_BY_STARTUP_TEMPERATURE( | 391 // the other two events above are sufficient for tracing purposes. |
| 392 UMA_HISTOGRAM_WITH_STARTUP_TEMPERATURE( |
| 360 UMA_HISTOGRAM_LONG_TIMES, "Startup.LoadTime.ProcessCreateToDllMain", | 393 UMA_HISTOGRAM_LONG_TIMES, "Startup.LoadTime.ProcessCreateToDllMain", |
| 361 dll_main_time - process_creation_time); | 394 dll_main_time - process_creation_time); |
| 362 } | 395 } |
| 363 } | 396 } |
| 364 } | 397 } |
| 365 | 398 |
| 366 void RecordBrowserWindowDisplay(const base::Time& time) { | 399 void RecordBrowserWindowDisplay(const base::Time& time) { |
| 367 static bool is_first_call = true; | 400 static bool is_first_call = true; |
| 368 if (!is_first_call || time.is_null()) | 401 if (!is_first_call || time.is_null()) |
| 369 return; | 402 return; |
| 370 is_first_call = false; | 403 is_first_call = false; |
| 371 if (WasNonBrowserUIDisplayed() || g_process_creation_time.Get().is_null()) | 404 if (WasNonBrowserUIDisplayed() || g_process_creation_time.Get().is_null()) |
| 372 return; | 405 return; |
| 373 | 406 |
| 374 UMA_HISTOGRAM_BY_STARTUP_TEMPERATURE( | 407 UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE( |
| 375 UMA_HISTOGRAM_LONG_TIMES, "Startup.BrowserWindowDisplay", | 408 UMA_HISTOGRAM_LONG_TIMES, "Startup.BrowserWindowDisplay", |
| 376 time - g_process_creation_time.Get()); | 409 g_process_creation_time.Get(), time); |
| 377 } | 410 } |
| 378 | 411 |
| 379 void RecordBrowserOpenTabsDelta(const base::TimeDelta& delta) { | 412 void RecordBrowserOpenTabsDelta(const base::TimeDelta& delta) { |
| 380 static bool is_first_call = true; | 413 static bool is_first_call = true; |
| 381 if (!is_first_call) | 414 if (!is_first_call) |
| 382 return; | 415 return; |
| 383 is_first_call = false; | 416 is_first_call = false; |
| 384 | 417 |
| 385 UMA_HISTOGRAM_BY_STARTUP_TEMPERATURE( | 418 UMA_HISTOGRAM_WITH_STARTUP_TEMPERATURE(UMA_HISTOGRAM_LONG_TIMES_100, |
| 386 UMA_HISTOGRAM_LONG_TIMES_100, "Startup.BrowserOpenTabs", delta); | 419 "Startup.BrowserOpenTabs", delta); |
| 387 } | 420 } |
| 388 | 421 |
| 389 void RecordFirstWebContentsMainFrameLoad(const base::Time& time) { | 422 void RecordFirstWebContentsMainFrameLoad(const base::Time& time) { |
| 390 static bool is_first_call = true; | 423 static bool is_first_call = true; |
| 391 if (!is_first_call || time.is_null()) | 424 if (!is_first_call || time.is_null()) |
| 392 return; | 425 return; |
| 393 is_first_call = false; | 426 is_first_call = false; |
| 394 if (WasNonBrowserUIDisplayed() || g_process_creation_time.Get().is_null()) | 427 if (WasNonBrowserUIDisplayed() || g_process_creation_time.Get().is_null()) |
| 395 return; | 428 return; |
| 396 | 429 |
| 397 UMA_HISTOGRAM_BY_STARTUP_TEMPERATURE( | 430 UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE( |
| 398 UMA_HISTOGRAM_LONG_TIMES_100, "Startup.FirstWebContents.MainFrameLoad", | 431 UMA_HISTOGRAM_LONG_TIMES_100, "Startup.FirstWebContents.MainFrameLoad", |
| 399 time - g_process_creation_time.Get()); | 432 g_process_creation_time.Get(), time); |
| 400 } | 433 } |
| 401 | 434 |
| 402 void RecordFirstWebContentsNonEmptyPaint(const base::Time& time) { | 435 void RecordFirstWebContentsNonEmptyPaint(const base::Time& time) { |
| 403 static bool is_first_call = true; | 436 static bool is_first_call = true; |
| 404 if (!is_first_call || time.is_null()) | 437 if (!is_first_call || time.is_null()) |
| 405 return; | 438 return; |
| 406 is_first_call = false; | 439 is_first_call = false; |
| 407 if (WasNonBrowserUIDisplayed() || g_process_creation_time.Get().is_null()) | 440 if (WasNonBrowserUIDisplayed() || g_process_creation_time.Get().is_null()) |
| 408 return; | 441 return; |
| 409 | 442 |
| 410 UMA_HISTOGRAM_BY_STARTUP_TEMPERATURE( | 443 UMA_HISTOGRAM_AND_TRACE_WITH_STARTUP_TEMPERATURE( |
| 411 UMA_HISTOGRAM_LONG_TIMES_100, "Startup.FirstWebContents.NonEmptyPaint", | 444 UMA_HISTOGRAM_LONG_TIMES_100, "Startup.FirstWebContents.NonEmptyPaint", |
| 412 time - g_process_creation_time.Get()); | 445 g_process_creation_time.Get(), time); |
| 413 } | 446 } |
| 414 | 447 |
| 415 base::Time MainEntryPointTime() { | 448 base::Time MainEntryPointTime() { |
| 416 return g_main_entry_point_time.Get(); | 449 return g_main_entry_point_time.Get(); |
| 417 } | 450 } |
| 418 | 451 |
| 419 StartupTemperature GetStartupTemperature() { | 452 StartupTemperature GetStartupTemperature() { |
| 420 return g_startup_temperature; | 453 return g_startup_temperature; |
| 421 } | 454 } |
| 422 | 455 |
| 423 } // namespace startup_metric_utils | 456 } // namespace startup_metric_utils |
| OLD | NEW |