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