• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3 // found in the LICENSE file.
4 
5 #include "components/startup_metric_utils/startup_metric_utils.h"
6 
7 #include "base/containers/hash_tables.h"
8 #include "base/environment.h"
9 #include "base/logging.h"
10 #include "base/metrics/histogram.h"
11 #include "base/metrics/histogram_base.h"
12 #include "base/metrics/statistics_recorder.h"
13 #include "base/process/process_info.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/synchronization/lock.h"
16 #include "base/sys_info.h"
17 #include "base/time/time.h"
18 
19 namespace {
20 
21 // 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.
23 volatile bool g_non_browser_ui_displayed = false;
24 
MainEntryPointTimeInternal()25 base::Time* MainEntryPointTimeInternal() {
26   static base::Time main_start_time = base::Time::Now();
27   return &main_start_time;
28 }
29 
30 typedef base::hash_map<std::string,base::TimeDelta> SubsystemStartupTimeHash;
31 
GetSubsystemStartupTimeHash()32 SubsystemStartupTimeHash* GetSubsystemStartupTimeHash() {
33   static SubsystemStartupTimeHash* slow_startup_time_hash =
34                                     new SubsystemStartupTimeHash;
35   return slow_startup_time_hash;
36 }
37 
GetSubsystemStartupTimeHashLock()38 base::Lock* GetSubsystemStartupTimeHashLock() {
39   static base::Lock* slow_startup_time_hash_lock = new base::Lock;
40   return slow_startup_time_hash_lock;
41 }
42 
43 // Record time of main entry so it can be read from Telemetry performance
44 // tests.
45 // TODO(jeremy): Remove once crbug.com/317481 is fixed.
RecordMainEntryTimeHistogram()46 void RecordMainEntryTimeHistogram() {
47   const int kLowWordMask = 0xFFFFFFFF;
48   const int kLower31BitsMask = 0x7FFFFFFF;
49   base::TimeDelta browser_main_entry_time_absolute =
50       base::TimeDelta::FromMilliseconds(
51           MainEntryPointTimeInternal()->ToInternalValue() / 1000.0);
52 
53   uint64 browser_main_entry_time_raw_ms =
54       browser_main_entry_time_absolute.InMilliseconds();
55 
56   base::TimeDelta browser_main_entry_time_raw_ms_high_word =
57       base::TimeDelta::FromMilliseconds(
58           (browser_main_entry_time_raw_ms >> 32) & kLowWordMask);
59   // Shift by one because histograms only support non-negative values.
60   base::TimeDelta browser_main_entry_time_raw_ms_low_word =
61       base::TimeDelta::FromMilliseconds(
62           (browser_main_entry_time_raw_ms >> 1) & kLower31BitsMask);
63 
64   // A timestamp is a 64 bit value, yet histograms can only store 32 bits.
65   HISTOGRAM_TIMES("Startup.BrowserMainEntryTimeAbsoluteHighWord",
66       browser_main_entry_time_raw_ms_high_word);
67   HISTOGRAM_TIMES("Startup.BrowserMainEntryTimeAbsoluteLowWord",
68       browser_main_entry_time_raw_ms_low_word);
69 }
70 
71 bool g_main_entry_time_was_recorded = false;
72 bool g_startup_stats_collection_finished = false;
73 bool g_was_slow_startup = false;
74 
75 // Environment variable that stores the timestamp when the executable's main()
76 // function was entered.
77 const char kChromeMainTimeEnvVar[] = "CHROME_MAIN_TIME";
78 
79 }  // namespace
80 
81 namespace startup_metric_utils {
82 
WasNonBrowserUIDisplayed()83 bool WasNonBrowserUIDisplayed() {
84   return g_non_browser_ui_displayed;
85 }
86 
SetNonBrowserUIDisplayed()87 void SetNonBrowserUIDisplayed() {
88   g_non_browser_ui_displayed = true;
89 }
90 
RecordMainEntryPointTime()91 void RecordMainEntryPointTime() {
92   DCHECK(!g_main_entry_time_was_recorded);
93   g_main_entry_time_was_recorded = true;
94   MainEntryPointTimeInternal();
95 }
96 
RecordExeMainEntryTime()97 void RecordExeMainEntryTime() {
98   std::string exe_load_time =
99       base::Int64ToString(base::Time::Now().ToInternalValue());
100   scoped_ptr<base::Environment> env(base::Environment::Create());
101   env->SetVar(kChromeMainTimeEnvVar, exe_load_time);
102 }
103 
104 #if defined(OS_ANDROID)
RecordSavedMainEntryPointTime(const base::Time & entry_point_time)105 void RecordSavedMainEntryPointTime(const base::Time& entry_point_time) {
106   DCHECK(!g_main_entry_time_was_recorded);
107   g_main_entry_time_was_recorded = true;
108   *MainEntryPointTimeInternal() = entry_point_time;
109 }
110 #endif // OS_ANDROID
111 
112 // Return the time recorded by RecordMainEntryPointTime().
MainEntryStartTime()113 const base::Time MainEntryStartTime() {
114   DCHECK(g_main_entry_time_was_recorded);
115   return *MainEntryPointTimeInternal();
116 }
117 
OnBrowserStartupComplete(bool is_first_run)118 void OnBrowserStartupComplete(bool is_first_run) {
119   RecordMainEntryTimeHistogram();
120 
121   // Bail if uptime < 7 minutes, to filter out cases where Chrome may have been
122   // autostarted and the machine is under io pressure.
123   const int64 kSevenMinutesInMilliseconds =
124       base::TimeDelta::FromMinutes(7).InMilliseconds();
125   if (base::SysInfo::Uptime() < kSevenMinutesInMilliseconds) {
126     g_startup_stats_collection_finished = true;
127     return;
128   }
129 
130   // The Startup.BrowserMessageLoopStartTime histogram recorded in
131   // chrome_browser_main.cc exhibits instability in the field which limits its
132   // usefulness in all scenarios except when we have a very large sample size.
133   // Attempt to mitigate this with a new metric:
134   // * Measure time from main entry rather than the OS' notion of process start
135   //   time.
136   // * Only measure launches that occur 7 minutes after boot to try to avoid
137   //   cases where Chrome is auto-started and IO is heavily loaded.
138   base::TimeDelta startup_time_from_main_entry =
139       base::Time::Now() - MainEntryStartTime();
140   if (is_first_run) {
141     UMA_HISTOGRAM_LONG_TIMES(
142         "Startup.BrowserMessageLoopStartTimeFromMainEntry.FirstRun",
143         startup_time_from_main_entry);
144   } else {
145     UMA_HISTOGRAM_LONG_TIMES(
146         "Startup.BrowserMessageLoopStartTimeFromMainEntry",
147         startup_time_from_main_entry);
148   }
149 
150 // CurrentProcessInfo::CreationTime() is currently only implemented on some
151 // platforms.
152 #if (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_WIN) || \
153     defined(OS_LINUX)
154   // Record timings between process creation, the main() in the executable being
155   // reached and the main() in the shared library being reached.
156   scoped_ptr<base::Environment> env(base::Environment::Create());
157   std::string chrome_main_entry_time_string;
158   if (env->GetVar(kChromeMainTimeEnvVar, &chrome_main_entry_time_string)) {
159     // The time that the Chrome executable's main() function was entered.
160     int64 chrome_main_entry_time_int = 0;
161     if (base::StringToInt64(chrome_main_entry_time_string,
162                             &chrome_main_entry_time_int)) {
163       base::Time process_create_time = base::CurrentProcessInfo::CreationTime();
164       base::Time exe_main_time =
165           base::Time::FromInternalValue(chrome_main_entry_time_int);
166       base::Time dll_main_time = MainEntryStartTime();
167 
168       // Process create to chrome.exe:main().
169       UMA_HISTOGRAM_LONG_TIMES("Startup.LoadTime.ProcessCreateToExeMain",
170                                exe_main_time - process_create_time);
171 
172       // chrome.exe:main() to chrome.dll:main().
173       UMA_HISTOGRAM_LONG_TIMES("Startup.LoadTime.ExeMainToDllMain",
174                                dll_main_time - exe_main_time);
175 
176       // Process create to chrome.dll:main().
177       UMA_HISTOGRAM_LONG_TIMES("Startup.LoadTime.ProcessCreateToDllMain",
178                                dll_main_time - process_create_time);
179     }
180   }
181 #endif
182 
183   // Record histograms for the subsystem times for startups > 10 seconds.
184   const base::TimeDelta kTenSeconds = base::TimeDelta::FromSeconds(10);
185   if (startup_time_from_main_entry < kTenSeconds) {
186     g_startup_stats_collection_finished = true;
187     return;
188   }
189 
190   // If we got here this was what we consider to be a slow startup which we
191   // want to record stats for.
192   g_was_slow_startup = true;
193 }
194 
OnInitialPageLoadComplete()195 void OnInitialPageLoadComplete() {
196   if (!g_was_slow_startup)
197     return;
198   DCHECK(!g_startup_stats_collection_finished);
199 
200   const base::TimeDelta kStartupTimeMin(
201       base::TimeDelta::FromMilliseconds(1));
202   const base::TimeDelta kStartupTimeMax(base::TimeDelta::FromMinutes(5));
203   static const size_t kStartupTimeBuckets = 100;
204 
205   // Set UMA flag for histograms outside chrome/ that can't use the
206   // ScopedSlowStartupUMA class.
207   base::HistogramBase* histogram =
208       base::StatisticsRecorder::FindHistogram("Startup.SlowStartupNSSInit");
209   if (histogram)
210     histogram->SetFlags(base::HistogramBase::kUmaTargetedHistogramFlag);
211 
212   // Iterate over the stats recorded by ScopedSlowStartupUMA and create
213   // histograms for them.
214   base::AutoLock locker(*GetSubsystemStartupTimeHashLock());
215   SubsystemStartupTimeHash* time_hash = GetSubsystemStartupTimeHash();
216   for (SubsystemStartupTimeHash::iterator i = time_hash->begin();
217       i != time_hash->end();
218       ++i) {
219     const std::string histogram_name = i->first;
220     base::HistogramBase* counter = base::Histogram::FactoryTimeGet(
221         histogram_name,
222         kStartupTimeMin,
223         kStartupTimeMax,
224         kStartupTimeBuckets,
225         base::Histogram::kUmaTargetedHistogramFlag);
226     counter->AddTime(i->second);
227   }
228 
229   g_startup_stats_collection_finished = true;
230 }
231 
~ScopedSlowStartupUMA()232 ScopedSlowStartupUMA::~ScopedSlowStartupUMA() {
233   if (g_startup_stats_collection_finished)
234     return;
235 
236   base::AutoLock locker(*GetSubsystemStartupTimeHashLock());
237   SubsystemStartupTimeHash* hash = GetSubsystemStartupTimeHash();
238   // Only record the initial sample for a given histogram.
239   if (hash->find(histogram_name_) !=  hash->end())
240     return;
241 
242   (*hash)[histogram_name_] =
243       base::TimeTicks::Now() - start_time_;
244 }
245 
246 }  // namespace startup_metric_utils
247