Home | History | Annotate | Download | only in metrics
      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 <string>
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/callback.h"
     10 #include "base/command_line.h"
     11 #include "base/compiler_specific.h"
     12 #include "base/metrics/histogram.h"
     13 #include "base/rand_util.h"
     14 #include "base/strings/string_number_conversions.h"
     15 #include "base/threading/sequenced_worker_pool.h"
     16 #include "chrome/browser/metrics/perf_provider_chromeos.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/browser/ui/browser.h"
     19 #include "chrome/browser/ui/browser_list.h"
     20 #include "chrome/browser/ui/browser_list_observer.h"
     21 #include "chrome/common/chrome_switches.h"
     22 #include "chromeos/dbus/dbus_thread_manager.h"
     23 #include "chromeos/dbus/debug_daemon_client.h"
     24 #include "content/public/browser/browser_thread.h"
     25 
     26 namespace {
     27 
     28 // Default time in seconds between invocations of perf.
     29 // This period is roughly 6.5 hours.
     30 // This is chosen to be relatively prime with the number of seconds in:
     31 // - one minute (60)
     32 // - one hour (3600)
     33 // - one day (86400)
     34 const size_t kPerfCommandIntervalDefaultSeconds = 23093;
     35 
     36 // The first collection interval is different from the interval above. This is
     37 // because we want to collect the first profile quickly after Chrome is started.
     38 // If this period is too long, the user will log off and Chrome will be killed
     39 // before it is triggered. The following 2 variables determine the upper and
     40 // lower bound on the interval.
     41 // The reason we do not always want to collect the initial profile after a fixed
     42 // period is to not over-represent task X in the profile where task X always
     43 // runs at a fixed period after start-up. By selecting a period randomly between
     44 // a lower and upper bound, we will hopefully collect a more fair profile.
     45 const size_t kPerfCommandStartIntervalLowerBoundMinutes = 10;
     46 
     47 const size_t kPerfCommandStartIntervalUpperBoundMinutes = 20;
     48 
     49 const size_t kNumberOfSecondsInAMinute = 60;
     50 
     51 // Default time in seconds perf is run for.
     52 const size_t kPerfCommandDurationDefaultSeconds = 2;
     53 
     54 // Enumeration representing success and various failure modes for collecting and
     55 // sending perf data.
     56 enum GetPerfDataOutcome {
     57   SUCCESS,
     58   NOT_READY_TO_UPLOAD,
     59   NOT_READY_TO_COLLECT,
     60   INCOGNITO_ACTIVE,
     61   INCOGNITO_LAUNCHED,
     62   PROTOBUF_NOT_PARSED,
     63   NUM_OUTCOMES
     64 };
     65 
     66 // Name of the histogram that represents the success and various failure modes
     67 // for collecting and sending perf data.
     68 const char kGetPerfDataOutcomeHistogram[] = "UMA.Perf.GetData";
     69 
     70 void AddToPerfHistogram(GetPerfDataOutcome outcome) {
     71   UMA_HISTOGRAM_ENUMERATION(kGetPerfDataOutcomeHistogram,
     72                             outcome,
     73                             NUM_OUTCOMES);
     74 }
     75 
     76 } // namespace
     77 
     78 
     79 namespace metrics {
     80 
     81 // This class must be created and used on the UI thread. It watches for any
     82 // incognito window being opened from the time it is instantiated to the time it
     83 // is destroyed.
     84 class WindowedIncognitoObserver : public chrome::BrowserListObserver {
     85  public:
     86   WindowedIncognitoObserver() : incognito_launched_(false) {
     87     BrowserList::AddObserver(this);
     88   }
     89 
     90   virtual ~WindowedIncognitoObserver() {
     91     BrowserList::RemoveObserver(this);
     92   }
     93 
     94   // This method can be checked to see whether any incognito window has been
     95   // opened since the time this object was created.
     96   bool incognito_launched() {
     97     return incognito_launched_;
     98   }
     99 
    100  private:
    101   // chrome::BrowserListObserver implementation.
    102   virtual void OnBrowserAdded(Browser* browser) OVERRIDE {
    103     if (browser->profile()->IsOffTheRecord())
    104       incognito_launched_ = true;
    105   }
    106 
    107   bool incognito_launched_;
    108 };
    109 
    110 PerfProvider::PerfProvider()
    111       : state_(READY_TO_COLLECT),
    112       weak_factory_(this) {
    113   size_t collection_interval_minutes = base::RandInt(
    114       kPerfCommandStartIntervalLowerBoundMinutes,
    115       kPerfCommandStartIntervalUpperBoundMinutes);
    116   ScheduleCollection(base::TimeDelta::FromMinutes(collection_interval_minutes));
    117 }
    118 
    119 PerfProvider::~PerfProvider() {}
    120 
    121 bool PerfProvider::GetPerfData(PerfDataProto* perf_data_proto) {
    122   DCHECK(CalledOnValidThread());
    123   if (state_ != READY_TO_UPLOAD) {
    124     AddToPerfHistogram(NOT_READY_TO_UPLOAD);
    125     return false;
    126   }
    127 
    128   *perf_data_proto = perf_data_proto_;
    129   state_ = READY_TO_COLLECT;
    130 
    131   AddToPerfHistogram(SUCCESS);
    132   return true;
    133 }
    134 
    135 void PerfProvider::ScheduleCollection(const base::TimeDelta& interval) {
    136   DCHECK(CalledOnValidThread());
    137   if (timer_.IsRunning())
    138     return;
    139 
    140   timer_.Start(FROM_HERE, interval, this,
    141                &PerfProvider::CollectIfNecessaryAndReschedule);
    142 }
    143 
    144 void PerfProvider::CollectIfNecessary() {
    145   DCHECK(CalledOnValidThread());
    146   if (state_ != READY_TO_COLLECT) {
    147     AddToPerfHistogram(NOT_READY_TO_COLLECT);
    148     return;
    149   }
    150 
    151   // For privacy reasons, Chrome should only collect perf data if there is no
    152   // incognito session active (or gets spawned during the collection).
    153   if (BrowserList::IsOffTheRecordSessionActive()) {
    154     AddToPerfHistogram(INCOGNITO_ACTIVE);
    155     return;
    156   }
    157 
    158   scoped_ptr<WindowedIncognitoObserver> incognito_observer(
    159       new WindowedIncognitoObserver);
    160 
    161   chromeos::DebugDaemonClient* client =
    162       chromeos::DBusThreadManager::Get()->GetDebugDaemonClient();
    163 
    164   base::TimeDelta collection_duration = base::TimeDelta::FromSeconds(
    165       kPerfCommandDurationDefaultSeconds);
    166 
    167   client->GetPerfData(collection_duration.InSeconds(),
    168                       base::Bind(&PerfProvider::ParseProtoIfValid,
    169                                  weak_factory_.GetWeakPtr(),
    170                                  base::Passed(&incognito_observer)));
    171 }
    172 
    173 void PerfProvider::CollectIfNecessaryAndReschedule() {
    174   CollectIfNecessary();
    175   ScheduleCollection(
    176       base::TimeDelta::FromSeconds(kPerfCommandIntervalDefaultSeconds));
    177 }
    178 
    179 void PerfProvider::ParseProtoIfValid(
    180     scoped_ptr<WindowedIncognitoObserver> incognito_observer,
    181     const std::vector<uint8>& data) {
    182   DCHECK(CalledOnValidThread());
    183 
    184   if (incognito_observer->incognito_launched()) {
    185     AddToPerfHistogram(INCOGNITO_LAUNCHED);
    186     return;
    187   }
    188 
    189   if (!perf_data_proto_.ParseFromArray(data.data(), data.size())) {
    190     AddToPerfHistogram(PROTOBUF_NOT_PARSED);
    191     perf_data_proto_.Clear();
    192     return;
    193   }
    194 
    195   state_ = READY_TO_UPLOAD;
    196 }
    197 } // namespace metrics
    198