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
28class BookmarkModel;
29class BookmarkNode;
30class DictionaryValue;
31class ListValue;
32class HistogramSynchronizer;
33class MetricsLogBase;
34class PrefService;
35class TemplateURLModel;
36
37namespace webkit {
38namespace npapi {
39struct 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.
45struct _xmlNode;
46typedef struct _xmlNode xmlNode;
47typedef xmlNode* xmlNodePtr;
48
49
50class 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