1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/common/startup_metric_utils.h" 6 7 #include "base/containers/hash_tables.h" 8 #include "base/logging.h" 9 #include "base/metrics/histogram.h" 10 #include "base/metrics/histogram_base.h" 11 #include "base/metrics/statistics_recorder.h" 12 #include "base/synchronization/lock.h" 13 #include "base/sys_info.h" 14 #include "base/time/time.h" 15 16 namespace { 17 18 // Mark as volatile to defensively make sure usage is thread-safe. 19 // Note that at the time of this writing, access is only on the UI thread. 20 volatile bool g_non_browser_ui_displayed = false; 21 22 const base::Time* MainEntryPointTimeInternal() { 23 static base::Time main_start_time = base::Time::Now(); 24 return &main_start_time; 25 } 26 27 typedef base::hash_map<std::string,base::TimeDelta> SubsystemStartupTimeHash; 28 29 SubsystemStartupTimeHash* GetSubsystemStartupTimeHash() { 30 static SubsystemStartupTimeHash* slow_startup_time_hash = 31 new SubsystemStartupTimeHash; 32 return slow_startup_time_hash; 33 } 34 35 base::Lock* GetSubsystemStartupTimeHashLock() { 36 static base::Lock* slow_startup_time_hash_lock = new base::Lock; 37 return slow_startup_time_hash_lock; 38 } 39 40 bool g_main_entry_time_was_recorded = false; 41 bool g_startup_stats_collection_finished = false; 42 bool g_was_slow_startup = false; 43 44 } // namespace 45 46 namespace startup_metric_utils { 47 48 bool WasNonBrowserUIDisplayed() { 49 return g_non_browser_ui_displayed; 50 } 51 52 void SetNonBrowserUIDisplayed() { 53 g_non_browser_ui_displayed = true; 54 } 55 56 void RecordMainEntryPointTime() { 57 DCHECK(!g_main_entry_time_was_recorded); 58 g_main_entry_time_was_recorded = true; 59 MainEntryPointTimeInternal(); 60 } 61 62 // Return the time recorded by RecordMainEntryPointTime(). 63 const base::Time MainEntryStartTime() { 64 DCHECK(g_main_entry_time_was_recorded); 65 return *MainEntryPointTimeInternal(); 66 } 67 68 void OnBrowserStartupComplete(bool is_first_run) { 69 // Bail if uptime < 7 minutes, to filter out cases where Chrome may have been 70 // autostarted and the machine is under io pressure. 71 const int64 kSevenMinutesInMilliseconds = 72 base::TimeDelta::FromMinutes(7).InMilliseconds(); 73 if (base::SysInfo::Uptime() < kSevenMinutesInMilliseconds) { 74 g_startup_stats_collection_finished = true; 75 return; 76 } 77 78 // The Startup.BrowserMessageLoopStartTime histogram recorded in 79 // chrome_browser_main.cc exhibits instability in the field which limits its 80 // usefulness in all scenarios except when we have a very large sample size. 81 // Attempt to mitigate this with a new metric: 82 // * Measure time from main entry rather than the OS' notion of process start 83 // time. 84 // * Only measure launches that occur 7 minutes after boot to try to avoid 85 // cases where Chrome is auto-started and IO is heavily loaded. 86 base::TimeDelta startup_time_from_main_entry = 87 base::Time::Now() - MainEntryStartTime(); 88 if (is_first_run) { 89 UMA_HISTOGRAM_LONG_TIMES( 90 "Startup.BrowserMessageLoopStartTimeFromMainEntry.FirstRun", 91 startup_time_from_main_entry); 92 } else { 93 UMA_HISTOGRAM_LONG_TIMES( 94 "Startup.BrowserMessageLoopStartTimeFromMainEntry", 95 startup_time_from_main_entry); 96 } 97 98 // Record histograms for the subsystem times for startups > 10 seconds. 99 const base::TimeDelta kTenSeconds = base::TimeDelta::FromSeconds(10); 100 if (startup_time_from_main_entry < kTenSeconds) { 101 g_startup_stats_collection_finished = true; 102 return; 103 } 104 105 // If we got here this was what we consider to be a slow startup which we 106 // want to record stats for. 107 g_was_slow_startup = true; 108 } 109 110 void OnInitialPageLoadComplete() { 111 if (!g_was_slow_startup) 112 return; 113 DCHECK(!g_startup_stats_collection_finished); 114 115 const base::TimeDelta kStartupTimeMin( 116 base::TimeDelta::FromMilliseconds(1)); 117 const base::TimeDelta kStartupTimeMax(base::TimeDelta::FromMinutes(5)); 118 static const size_t kStartupTimeBuckets = 100; 119 120 // Set UMA flag for histograms outside chrome/ that can't use the 121 // ScopedSlowStartupUMA class. 122 base::HistogramBase* histogram = 123 base::StatisticsRecorder::FindHistogram("Startup.SlowStartupNSSInit"); 124 if (histogram) 125 histogram->SetFlags(base::HistogramBase::kUmaTargetedHistogramFlag); 126 127 // Iterate over the stats recorded by ScopedSlowStartupUMA and create 128 // histograms for them. 129 base::AutoLock locker(*GetSubsystemStartupTimeHashLock()); 130 SubsystemStartupTimeHash* time_hash = GetSubsystemStartupTimeHash(); 131 for (SubsystemStartupTimeHash::iterator i = time_hash->begin(); 132 i != time_hash->end(); 133 ++i) { 134 const std::string histogram_name = i->first; 135 base::HistogramBase* counter = base::Histogram::FactoryTimeGet( 136 histogram_name, 137 kStartupTimeMin, 138 kStartupTimeMax, 139 kStartupTimeBuckets, 140 base::Histogram::kUmaTargetedHistogramFlag); 141 counter->AddTime(i->second); 142 } 143 144 g_startup_stats_collection_finished = true; 145 } 146 147 ScopedSlowStartupUMA::~ScopedSlowStartupUMA() { 148 if (g_startup_stats_collection_finished) 149 return; 150 151 base::AutoLock locker(*GetSubsystemStartupTimeHashLock()); 152 SubsystemStartupTimeHash* hash = GetSubsystemStartupTimeHash(); 153 // Only record the initial sample for a given histogram. 154 if (hash->find(histogram_name_) != hash->end()) 155 return; 156 157 (*hash)[histogram_name_] = 158 base::TimeTicks::Now() - start_time_; 159 } 160 161 } // namespace startup_metric_utils 162