Home | History | Annotate | Download | only in metrics
      1 // Copyright (c) 2011 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 // This file defines a service that collects information about the user
      6 // experience in order to help improve future versions of the app.
      7 
      8 #ifndef CHROME_BROWSER_METRICS_METRICS_SERVICE_H_
      9 #define CHROME_BROWSER_METRICS_METRICS_SERVICE_H_
     10 #pragma once
     11 
     12 #include <map>
     13 #include <string>
     14 #include <vector>
     15 
     16 #include "base/basictypes.h"
     17 #include "base/gtest_prod_util.h"
     18 #include "base/memory/scoped_ptr.h"
     19 #include "chrome/common/metrics_helpers.h"
     20 #include "chrome/common/net/url_fetcher.h"
     21 #include "content/common/notification_observer.h"
     22 #include "content/common/notification_registrar.h"
     23 
     24 #if defined(OS_CHROMEOS)
     25 #include "chrome/browser/chromeos/external_metrics.h"
     26 #endif
     27 
     28 class BookmarkModel;
     29 class BookmarkNode;
     30 class DictionaryValue;
     31 class ListValue;
     32 class HistogramSynchronizer;
     33 class MetricsLogBase;
     34 class PrefService;
     35 class TemplateURLModel;
     36 
     37 namespace webkit {
     38 namespace npapi {
     39 struct WebPluginInfo;
     40 }
     41 }
     42 
     43 // Forward declaration of the xmlNode to avoid having tons of gyp files
     44 // needing to depend on the libxml third party lib.
     45 struct _xmlNode;
     46 typedef struct _xmlNode xmlNode;
     47 typedef xmlNode* xmlNodePtr;
     48 
     49 
     50 class MetricsService : public NotificationObserver,
     51                        public URLFetcher::Delegate,
     52                        public MetricsServiceBase {
     53  public:
     54   // Used to produce a historgram that keeps track of the status of recalling
     55   // persisted per logs.
     56   enum LogRecallStatus {
     57     RECALL_SUCCESS,         // We were able to correctly recall a persisted log.
     58     LIST_EMPTY,             // Attempting to recall from an empty list.
     59     LIST_SIZE_MISSING,      // Failed to recover list size using GetAsInteger().
     60     LIST_SIZE_TOO_SMALL,    // Too few elements in the list (less than 3).
     61     LIST_SIZE_CORRUPTION,   // List size is not as expected.
     62     LOG_STRING_CORRUPTION,  // Failed to recover log string using GetAsString().
     63     CHECKSUM_CORRUPTION,    // Failed to verify checksum.
     64     CHECKSUM_STRING_CORRUPTION,  // Failed to recover checksum string using
     65                                  // GetAsString().
     66     DECODE_FAIL,            // Failed to decode log.
     67     END_RECALL_STATUS       // Number of bins to use to create the histogram.
     68   };
     69 
     70   // TODO(ziadh): This is here temporarily for a side experiment. Remove later
     71   // on.
     72   enum LogStoreStatus {
     73     STORE_SUCCESS,    // Successfully presisted log.
     74     ENCODE_FAIL,      // Failed to encode log.
     75     COMPRESS_FAIL,    // Failed to compress log.
     76     END_STORE_STATUS  // Number of bins to use to create the histogram.
     77   };
     78 
     79   MetricsService();
     80   virtual ~MetricsService();
     81 
     82   // Start/stop the metrics recording and uploading machine.  These should be
     83   // used on startup and when the user clicks the checkbox in the prefs.
     84   // StartRecordingOnly starts the metrics recording but not reporting, for use
     85   // in tests only.
     86   void Start();
     87   void StartRecordingOnly();
     88   void Stop();
     89 
     90   // At startup, prefs needs to be called with a list of all the pref names and
     91   // types we'll be using.
     92   static void RegisterPrefs(PrefService* local_state);
     93 
     94   // Set up notifications which indicate that a user is performing work. This is
     95   // useful to allow some features to sleep, until the machine becomes active,
     96   // such as precluding UMA uploads unless there was recent activity.
     97   static void SetUpNotifications(NotificationRegistrar* registrar,
     98                                  NotificationObserver* observer);
     99 
    100   // Implementation of NotificationObserver
    101   virtual void Observe(NotificationType type,
    102                        const NotificationSource& source,
    103                        const NotificationDetails& details);
    104 
    105   // Invoked when we get a WM_SESSIONEND. This places a value in prefs that is
    106   // reset when RecordCompletedSessionEnd is invoked.
    107   void RecordStartOfSessionEnd();
    108 
    109   // This should be called when the application is shutting down. It records
    110   // that session end was successful.
    111   void RecordCompletedSessionEnd();
    112 
    113   // Saves in the preferences if the crash report registration was successful.
    114   // This count is eventually send via UMA logs.
    115   void RecordBreakpadRegistration(bool success);
    116 
    117   // Saves in the preferences if the browser is running under a debugger.
    118   // This count is eventually send via UMA logs.
    119   void RecordBreakpadHasDebugger(bool has_debugger);
    120 
    121   // Save any unsent logs into a persistent store in a pref.  We always do this
    122   // at shutdown, but we can do it as we reduce the list as well.
    123   void StoreUnsentLogs();
    124 
    125 #if defined(OS_CHROMEOS)
    126   // Start the external metrics service, which collects metrics from Chrome OS
    127   // and passes them to UMA.
    128   void StartExternalMetrics();
    129 
    130   // Records a Chrome OS crash.
    131   void LogChromeOSCrash(const std::string &crash_type);
    132 #endif
    133 
    134   bool recording_active() const;
    135   bool reporting_active() const;
    136 
    137  private:
    138   // The MetricsService has a lifecycle that is stored as a state.
    139   // See metrics_service.cc for description of this lifecycle.
    140   enum State {
    141     INITIALIZED,            // Constructor was called.
    142     INIT_TASK_SCHEDULED,    // Waiting for deferred init tasks to complete.
    143     INIT_TASK_DONE,         // Waiting for timer to send initial log.
    144     INITIAL_LOG_READY,      // Initial log generated, and waiting for reply.
    145     SEND_OLD_INITIAL_LOGS,  // Sending unsent logs from previous session.
    146     SENDING_OLD_LOGS,       // Sending unsent logs from previous session.
    147     SENDING_CURRENT_LOGS,   // Sending standard current logs as they acrue.
    148   };
    149 
    150   class InitTask;
    151   class InitTaskComplete;
    152 
    153   // Callback to let us know that the init task is done.
    154   void OnInitTaskComplete(
    155       const std::string& hardware_class,
    156       const std::vector<webkit::npapi::WebPluginInfo>& plugins);
    157 
    158   // When we start a new version of Chromium (different from our last run), we
    159   // need to discard the old crash stats so that we don't attribute crashes etc.
    160   // in the old version to the current version (via current logs).
    161   // Without this, a common reason to finally start a new version is to crash
    162   // the old version (after an autoupdate has arrived), and so we'd bias
    163   // initial results towards showing crashes :-(.
    164   static void DiscardOldStabilityStats(PrefService* local_state);
    165 
    166   // Sets and gets whether metrics recording is active.
    167   // SetRecording(false) also forces a persistent save of logging state (if
    168   // anything has been recorded, or transmitted).
    169   void SetRecording(bool enabled);
    170 
    171   // Enable/disable transmission of accumulated logs and crash reports (dumps).
    172   void SetReporting(bool enabled);
    173 
    174   // If in_idle is true, sets idle_since_last_transmission to true.
    175   // If in_idle is false and idle_since_last_transmission_ is true, sets
    176   // idle_since_last_transmission to false and starts the timer (provided
    177   // starting the timer is permitted).
    178   void HandleIdleSinceLastTransmission(bool in_idle);
    179 
    180   // Set up client ID, session ID, etc.
    181   void InitializeMetricsState();
    182 
    183   // Generates a new client ID to use to identify self to metrics server.
    184   static std::string GenerateClientID();
    185 
    186   // Schedule the next save of LocalState information.  This is called
    187   // automatically by the task that performs each save to schedule the next one.
    188   void ScheduleNextStateSave();
    189 
    190   // Save the LocalState information immediately. This should not be called by
    191   // anybody other than the scheduler to avoid doing too many writes. When you
    192   // make a change, call ScheduleNextStateSave() instead.
    193   void SaveLocalState();
    194 
    195   // Called to start recording user experience metrics.
    196   // Constructs a new, empty current_log_.
    197   void StartRecording();
    198 
    199   // Called to stop recording user experience metrics.  The caller takes
    200   // ownership of the resulting MetricsLog object via the log parameter,
    201   // or passes in NULL to indicate that the log should simply be deleted.
    202   void StopRecording(MetricsLogBase** log);
    203 
    204   // Deletes pending_log_ and current_log_, and pushes their text into the
    205   // appropriate unsent_log vectors.  Called when Chrome shuts down.
    206   void PushPendingLogsToUnsentLists();
    207 
    208   // Save the pending_log_text_ persistently in a pref for transmission when we
    209   // next run.  Note that IF this text is "too large," we just dicard it.
    210   void PushPendingLogTextToUnsentOngoingLogs();
    211 
    212   // Start timer for next log transmission.
    213   void StartLogTransmissionTimer();
    214 
    215   // Internal function to collect process memory information.
    216   void LogTransmissionTimerDone();
    217 
    218   // Do not call OnMemoryDetailCollectionDone() or
    219   // OnHistogramSynchronizationDone() directly.
    220   // Use StartLogTransmissionTimer() to schedule a call.
    221   void OnMemoryDetailCollectionDone();
    222   void OnHistogramSynchronizationDone();
    223 
    224   // Takes whatever log should be uploaded next (according to the state_)
    225   // and makes it the pending log.  If pending_log_ is not NULL,
    226   // MakePendingLog does nothing and returns.
    227   void MakePendingLog();
    228 
    229   // Determines from state_ and permissions set out by the server whether the
    230   // pending_log_ should be sent or discarded.
    231   bool ServerPermitsTransmission() const;
    232 
    233   // Check to see if there are any unsent logs from previous sessions.
    234   bool unsent_logs() const {
    235     return !unsent_initial_logs_.empty() || !unsent_ongoing_logs_.empty();
    236   }
    237   // Record stats, client ID, Session ID, etc. in a special "first" log.
    238   void PrepareInitialLog();
    239   // Pull copies of unsent logs from prefs into instance variables.
    240   void RecallUnsentLogs();
    241   // Decode and verify written pref log data.
    242   static MetricsService::LogRecallStatus RecallUnsentLogsHelper(
    243       const ListValue& list,
    244       std::vector<std::string>* local_list);
    245   // Encode and write list size and checksum for perf log data.
    246   static void StoreUnsentLogsHelper(const std::vector<std::string>& local_list,
    247                                     const size_t kMaxLocalListSize,
    248                                     ListValue* list);
    249   // Convert |pending_log_| to XML in |compressed_log_|, and compress it for
    250   // transmission.
    251   void PreparePendingLogText();
    252 
    253   // Convert pending_log_ to XML, compress it, and prepare to pass to server.
    254   // Upon return, current_fetch_ should be reset with its upload data set to
    255   // a compressed copy of the pending log.
    256   void PrepareFetchWithPendingLog();
    257 
    258   // Implementation of URLFetcher::Delegate. Called after transmission
    259   // completes (either successfully or with failure).
    260   virtual void OnURLFetchComplete(const URLFetcher* source,
    261                                   const GURL& url,
    262                                   const net::URLRequestStatus& status,
    263                                   int response_code,
    264                                   const ResponseCookies& cookies,
    265                                   const std::string& data);
    266 
    267   // Called by OnURLFetchComplete to handle the case when the server returned
    268   // a response code not equal to 200.
    269   void HandleBadResponseCode();
    270 
    271   // Records a window-related notification.
    272   void LogWindowChange(NotificationType type,
    273                        const NotificationSource& source,
    274                        const NotificationDetails& details);
    275 
    276   // Reads, increments and then sets the specified integer preference.
    277   void IncrementPrefValue(const char* path);
    278 
    279   // Reads, increments and then sets the specified long preference that is
    280   // stored as a string.
    281   void IncrementLongPrefsValue(const char* path);
    282 
    283   // Records a renderer process crash.
    284   void LogRendererCrash();
    285 
    286   // Records an extension renderer process crash.
    287   void LogExtensionRendererCrash();
    288 
    289   // Records a renderer process hang.
    290   void LogRendererHang();
    291 
    292   // Records that the browser was shut down cleanly.
    293   void LogCleanShutdown();
    294 
    295   // Set the value in preferences for the number of bookmarks and folders
    296   // in node. The pref key for the number of bookmarks in num_bookmarks_key and
    297   // the pref key for number of folders in num_folders_key.
    298   void LogBookmarks(const BookmarkNode* node,
    299                     const char* num_bookmarks_key,
    300                     const char* num_folders_key);
    301 
    302   // Sets preferences for the number of bookmarks in model.
    303   void LogBookmarks(BookmarkModel* model);
    304 
    305   // Records a child process related notification.  These are recorded to an
    306   // in-object buffer because these notifications are sent on page load, and we
    307   // don't want to slow that down.
    308   void LogChildProcessChange(NotificationType type,
    309                              const NotificationSource& source,
    310                              const NotificationDetails& details);
    311 
    312   // Logs keywords specific metrics. Keyword metrics are recorded in the
    313   // profile specific metrics.
    314   void LogKeywords(const TemplateURLModel* url_model);
    315 
    316   // Saves plugin-related updates from the in-object buffer to Local State
    317   // for retrieval next time we send a Profile log (generally next launch).
    318   void RecordPluginChanges(PrefService* pref);
    319 
    320   // Records state that should be periodically saved, like uptime and
    321   // buffered plugin stability statistics.
    322   void RecordCurrentState(PrefService* pref);
    323 
    324   // Logs the initiation of a page load
    325   void LogLoadStarted();
    326 
    327   // Records a page load notification.
    328   void LogLoadComplete(NotificationType type,
    329                        const NotificationSource& source,
    330                        const NotificationDetails& details);
    331 
    332   // Checks whether a notification can be logged.
    333   bool CanLogNotification(NotificationType type,
    334                           const NotificationSource& source,
    335                           const NotificationDetails& details);
    336 
    337   // Sets the value of the specified path in prefs and schedules a save.
    338   void RecordBooleanPrefValue(const char* path, bool value);
    339 
    340   NotificationRegistrar registrar_;
    341 
    342   // Indicate whether recording and reporting are currently happening.
    343   // These should not be set directly, but by calling SetRecording and
    344   // SetReporting.
    345   bool recording_active_;
    346   bool reporting_active_;
    347 
    348   // The variable server_permits_upload_ is set true when the response
    349   // data forbids uploading.  This should coinside with the "die roll"
    350   // with probability in the upload tag of the response data came out
    351   // affirmative.
    352   bool server_permits_upload_;
    353 
    354   // The progession of states made by the browser are recorded in the following
    355   // state.
    356   State state_;
    357 
    358   // Chrome OS hardware class (e.g., hardware qualification ID). This
    359   // class identifies the configured system components such as CPU,
    360   // WiFi adapter, etc.  For non Chrome OS hosts, this will be an
    361   // empty string.
    362   std::string hardware_class_;
    363 
    364   // The list of plugins which was retrieved on the file thread.
    365   std::vector<webkit::npapi::WebPluginInfo> plugins_;
    366 
    367   // The outstanding transmission appears as a URL Fetch operation.
    368   scoped_ptr<URLFetcher> current_fetch_;
    369 
    370   // The URL for the metrics server.
    371   std::wstring server_url_;
    372 
    373   // The identifier that's sent to the server with the log reports.
    374   std::string client_id_;
    375 
    376   // Whether the MetricsService object has received any notifications since
    377   // the last time a transmission was sent.
    378   bool idle_since_last_transmission_;
    379 
    380   // A number that identifies the how many times the app has been launched.
    381   int session_id_;
    382 
    383   // When logs were not sent during a previous session they are queued to be
    384   // sent instead of currently accumulating logs.  We give preference to sending
    385   // our inital log first, then unsent intial logs, then unsent ongoing logs.
    386   // Unsent logs are gathered at shutdown, and save in a persistent pref, one
    387   // log in each string in the following arrays.
    388   // Note that the vector has the oldest logs listed first (early in the
    389   // vector), and we'll discard old logs if we have gathered too many logs.
    390   std::vector<std::string> unsent_initial_logs_;
    391   std::vector<std::string> unsent_ongoing_logs_;
    392 
    393   // Maps NavigationControllers (corresponding to tabs) or Browser
    394   // (corresponding to Windows) to a unique integer that we will use to identify
    395   // it. |next_window_id_| is used to track which IDs we have used so far.
    396   typedef std::map<uintptr_t, int> WindowMap;
    397   WindowMap window_map_;
    398   int next_window_id_;
    399 
    400   // Buffer of child process notifications for quick access.  See
    401   // ChildProcessStats documentation above for more details.
    402   struct ChildProcessStats;
    403   std::map<std::wstring, ChildProcessStats> child_process_stats_buffer_;
    404 
    405   ScopedRunnableMethodFactory<MetricsService> log_sender_factory_;
    406   ScopedRunnableMethodFactory<MetricsService> state_saver_factory_;
    407 
    408   // Dictionary containing all the profile specific metrics. This is set
    409   // at creation time from the prefs.
    410   scoped_ptr<DictionaryValue> profile_dictionary_;
    411 
    412   // The interval between consecutive log transmissions (to avoid hogging the
    413   // outbound network link).  This is usually also the duration for which we
    414   // build up a log, but if other unsent-logs from previous sessions exist, we
    415   // quickly transmit those unsent logs while we continue to build a log.
    416   base::TimeDelta interlog_duration_;
    417 
    418   // Indicate that a timer for sending the next log has already been queued.
    419   bool timer_pending_;
    420 
    421 #if defined(OS_CHROMEOS)
    422   // The external metric service is used to log ChromeOS UMA events.
    423   scoped_refptr<chromeos::ExternalMetrics> external_metrics_;
    424 #endif
    425 
    426   FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, EmptyLogList);
    427   FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, SingleElementLogList);
    428   FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, OverLimitLogList);
    429   FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, SmallRecoveredListSize);
    430   FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, RemoveSizeFromLogList);
    431   FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, CorruptSizeOfLogList);
    432   FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, CorruptChecksumOfLogList);
    433   FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, ClientIdGeneratesAllZeroes);
    434   FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, ClientIdGeneratesCorrectly);
    435   FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, ClientIdCorrectlyFormatted);
    436 
    437   DISALLOW_COPY_AND_ASSIGN(MetricsService);
    438 };
    439 
    440 #endif  // CHROME_BROWSER_METRICS_METRICS_SERVICE_H_
    441