• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
unsent_logs()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