Home | History | Annotate | Download | only in startup_metric_utils
      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 
     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 
     32 SubsystemStartupTimeHash* GetSubsystemStartupTimeHash() {
     33   static SubsystemStartupTimeHash* slow_startup_time_hash =
     34                                     new SubsystemStartupTimeHash;
     35   return slow_startup_time_hash;
     36 }
     37 
     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.
     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 
     83 bool WasNonBrowserUIDisplayed() {
     84   return g_non_browser_ui_displayed;
     85 }
     86 
     87 void SetNonBrowserUIDisplayed() {
     88   g_non_browser_ui_displayed = true;
     89 }
     90 
     91 void RecordMainEntryPointTime() {
     92   DCHECK(!g_main_entry_time_was_recorded);
     93   g_main_entry_time_was_recorded = true;
     94   MainEntryPointTimeInternal();
     95 }
     96 
     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)
    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().
    113 const base::Time MainEntryStartTime() {
    114   DCHECK(g_main_entry_time_was_recorded);
    115   return *MainEntryPointTimeInternal();
    116 }
    117 
    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 
    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 
    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