Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 //------------------------------------------------------------------------------ | 5 #include "chrome/browser/metrics/metrics_state_manager.h" |
| 6 // Description of the life cycle of a instance of MetricsService. | |
| 7 // | |
| 8 // OVERVIEW | |
| 9 // | |
| 10 // A MetricsService instance is typically created at application startup. It is | |
| 11 // the central controller for the acquisition of log data, and the automatic | |
| 12 // transmission of that log data to an external server. Its major job is to | |
| 13 // manage logs, grouping them for transmission, and transmitting them. As part | |
| 14 // of its grouping, MS finalizes logs by including some just-in-time gathered | |
| 15 // memory statistics, snapshotting the current stats of numerous histograms, | |
| 16 // closing the logs, translating to protocol buffer format, and compressing the | |
| 17 // results for transmission. Transmission includes submitting a compressed log | |
| 18 // as data in a URL-post, and retransmitting (or retaining at process | |
| 19 // termination) if the attempted transmission failed. Retention across process | |
| 20 // terminations is done using the the PrefServices facilities. The retained logs | |
| 21 // (the ones that never got transmitted) are compressed and base64-encoded | |
| 22 // before being persisted. | |
| 23 // | |
| 24 // Logs fall into one of two categories: "initial logs," and "ongoing logs." | |
| 25 // There is at most one initial log sent for each complete run of Chrome (from | |
| 26 // startup, to browser shutdown). An initial log is generally transmitted some | |
| 27 // short time (1 minute?) after startup, and includes stats such as recent crash | |
| 28 // info, the number and types of plugins, etc. The external server's response | |
| 29 // to the initial log conceptually tells this MS if it should continue | |
| 30 // transmitting logs (during this session). The server response can actually be | |
| 31 // much more detailed, and always includes (at a minimum) how often additional | |
| 32 // ongoing logs should be sent. | |
| 33 // | |
| 34 // After the above initial log, a series of ongoing logs will be transmitted. | |
| 35 // The first ongoing log actually begins to accumulate information stating when | |
| 36 // the MS was first constructed. Note that even though the initial log is | |
| 37 // commonly sent a full minute after startup, the initial log does not include | |
| 38 // much in the way of user stats. The most common interlog period (delay) | |
| 39 // is 30 minutes. That time period starts when the first user action causes a | |
| 40 // logging event. This means that if there is no user action, there may be long | |
| 41 // periods without any (ongoing) log transmissions. Ongoing logs typically | |
| 42 // contain very detailed records of user activities (ex: opened tab, closed | |
| 43 // tab, fetched URL, maximized window, etc.) In addition, just before an | |
| 44 // ongoing log is closed out, a call is made to gather memory statistics. Those | |
| 45 // memory statistics are deposited into a histogram, and the log finalization | |
| 46 // code is then called. In the finalization, a call to a Histogram server | |
| 47 // acquires a list of all local histograms that have been flagged for upload | |
| 48 // to the UMA server. The finalization also acquires the most recent number | |
| 49 // of page loads, along with any counts of renderer or plugin crashes. | |
| 50 // | |
| 51 // When the browser shuts down, there will typically be a fragment of an ongoing | |
| 52 // log that has not yet been transmitted. At shutdown time, that fragment is | |
| 53 // closed (including snapshotting histograms), and persisted, for potential | |
| 54 // transmission during a future run of the product. | |
| 55 // | |
| 56 // There are two slightly abnormal shutdown conditions. There is a | |
| 57 // "disconnected scenario," and a "really fast startup and shutdown" scenario. | |
| 58 // In the "never connected" situation, the user has (during the running of the | |
| 59 // process) never established an internet connection. As a result, attempts to | |
| 60 // transmit the initial log have failed, and a lot(?) of data has accumulated in | |
| 61 // the ongoing log (which didn't yet get closed, because there was never even a | |
| 62 // contemplation of sending it). There is also a kindred "lost connection" | |
| 63 // situation, where a loss of connection prevented an ongoing log from being | |
| 64 // transmitted, and a (still open) log was stuck accumulating a lot(?) of data, | |
| 65 // while the earlier log retried its transmission. In both of these | |
| 66 // disconnected situations, two logs need to be, and are, persistently stored | |
| 67 // for future transmission. | |
| 68 // | |
| 69 // The other unusual shutdown condition, termed "really fast startup and | |
| 70 // shutdown," involves the deliberate user termination of the process before | |
| 71 // the initial log is even formed or transmitted. In that situation, no logging | |
| 72 // is done, but the historical crash statistics remain (unlogged) for inclusion | |
| 73 // in a future run's initial log. (i.e., we don't lose crash stats). | |
| 74 // | |
| 75 // With the above overview, we can now describe the state machine's various | |
| 76 // states, based on the State enum specified in the state_ member. Those states | |
| 77 // are: | |
| 78 // | |
| 79 // INITIALIZED, // Constructor was called. | |
| 80 // INIT_TASK_SCHEDULED, // Waiting for deferred init tasks to finish. | |
| 81 // INIT_TASK_DONE, // Waiting for timer to send initial log. | |
| 82 // SENDING_INITIAL_STABILITY_LOG, // Initial stability log being sent. | |
| 83 // SENDING_INITIAL_METRICS_LOG, // Initial metrics log being sent. | |
| 84 // SENDING_OLD_LOGS, // Sending unsent logs from previous session. | |
| 85 // SENDING_CURRENT_LOGS, // Sending ongoing logs as they acrue. | |
| 86 // | |
| 87 // In more detail, we have: | |
| 88 // | |
| 89 // INITIALIZED, // Constructor was called. | |
| 90 // The MS has been constructed, but has taken no actions to compose the | |
| 91 // initial log. | |
| 92 // | |
| 93 // INIT_TASK_SCHEDULED, // Waiting for deferred init tasks to finish. | |
| 94 // Typically about 30 seconds after startup, a task is sent to a second thread | |
| 95 // (the file thread) to perform deferred (lower priority and slower) | |
| 96 // initialization steps such as getting the list of plugins. That task will | |
| 97 // (when complete) make an async callback (via a Task) to indicate the | |
| 98 // completion. | |
| 99 // | |
| 100 // INIT_TASK_DONE, // Waiting for timer to send initial log. | |
| 101 // The callback has arrived, and it is now possible for an initial log to be | |
| 102 // created. This callback typically arrives back less than one second after | |
| 103 // the deferred init task is dispatched. | |
| 104 // | |
| 105 // SENDING_INITIAL_STABILITY_LOG, // Initial stability log being sent. | |
| 106 // During initialization, if a crash occurred during the previous session, an | |
| 107 // initial stability log will be generated and registered with the log manager. | |
| 108 // This state will be entered if a stability log was prepared during metrics | |
| 109 // service initialization (in InitializeMetricsRecordingState()) and is waiting | |
| 110 // to be transmitted when it's time to send up the first log (per the reporting | |
| 111 // scheduler). If there is no initial stability log (e.g. there was no previous | |
| 112 // crash), then this state will be skipped and the state will advance to | |
| 113 // SENDING_INITIAL_METRICS_LOG. | |
| 114 // | |
| 115 // SENDING_INITIAL_METRICS_LOG, // Initial metrics log being sent. | |
| 116 // This state is entered after the initial metrics log has been composed, and | |
| 117 // prepared for transmission. This happens after SENDING_INITIAL_STABILITY_LOG | |
| 118 // if there was an initial stability log (see above). It is also the case that | |
| 119 // any previously unsent logs have been loaded into instance variables for | |
| 120 // possible transmission. | |
| 121 // | |
| 122 // SENDING_OLD_LOGS, // Sending unsent logs from previous session. | |
| 123 // This state indicates that the initial log for this session has been | |
| 124 // successfully sent and it is now time to send any logs that were | |
| 125 // saved from previous sessions. All such logs will be transmitted before | |
| 126 // exiting this state, and proceeding with ongoing logs from the current session | |
| 127 // (see next state). | |
| 128 // | |
| 129 // SENDING_CURRENT_LOGS, // Sending standard current logs as they accrue. | |
| 130 // Current logs are being accumulated. Typically every 20 minutes a log is | |
| 131 // closed and finalized for transmission, at the same time as a new log is | |
| 132 // started. | |
| 133 // | |
| 134 // The progression through the above states is simple, and sequential, in the | |
| 135 // most common use cases. States proceed from INITIAL to SENDING_CURRENT_LOGS, | |
| 136 // and remain in the latter until shutdown. | |
| 137 // | |
| 138 // The one unusual case is when the user asks that we stop logging. When that | |
| 139 // happens, any staged (transmission in progress) log is persisted, and any log | |
| 140 // that is currently accumulating is also finalized and persisted. We then | |
| 141 // regress back to the SEND_OLD_LOGS state in case the user enables log | |
| 142 // recording again during this session. This way anything we have persisted | |
| 143 // will be sent automatically if/when we progress back to SENDING_CURRENT_LOG | |
| 144 // state. | |
| 145 // | |
| 146 // Another similar case is on mobile, when the application is backgrounded and | |
| 147 // then foregrounded again. Backgrounding created new "old" stored logs, so the | |
| 148 // state drops back from SENDING_CURRENT_LOGS to SENDING_OLD_LOGS so those logs | |
| 149 // will be sent. | |
| 150 // | |
| 151 // Also note that whenever we successfully send an old log, we mirror the list | |
| 152 // of logs into the PrefService. This ensures that IF we crash, we won't start | |
| 153 // up and retransmit our old logs again. | |
| 154 // | |
| 155 // Due to race conditions, it is always possible that a log file could be sent | |
| 156 // twice. For example, if a log file is sent, but not yet acknowledged by | |
| 157 // the external server, and the user shuts down, then a copy of the log may be | |
| 158 // saved for re-transmission. These duplicates could be filtered out server | |
| 159 // side, but are not expected to be a significant problem. | |
| 160 // | |
| 161 // | |
| 162 //------------------------------------------------------------------------------ | |
| 163 | 6 |
| 164 #include "chrome/browser/metrics/metrics_service.h" | |
| 165 | |
| 166 #include <algorithm> | |
| 167 | |
| 168 #include "base/bind.h" | |
| 169 #include "base/callback.h" | |
| 170 #include "base/command_line.h" | 7 #include "base/command_line.h" |
| 171 #include "base/guid.h" | 8 #include "base/guid.h" |
| 172 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
| 173 #include "base/metrics/sparse_histogram.h" | 10 #include "base/metrics/sparse_histogram.h" |
| 174 #include "base/metrics/statistics_recorder.h" | |
| 175 #include "base/prefs/pref_registry_simple.h" | 11 #include "base/prefs/pref_registry_simple.h" |
| 176 #include "base/prefs/pref_service.h" | 12 #include "base/prefs/pref_service.h" |
| 177 #include "base/prefs/scoped_user_pref_update.h" | |
| 178 #include "base/rand_util.h" | 13 #include "base/rand_util.h" |
| 179 #include "base/strings/string_number_conversions.h" | 14 #include "base/strings/string_number_conversions.h" |
| 180 #include "base/strings/utf_string_conversions.h" | 15 #include "base/time/time.h" |
| 181 #include "base/threading/platform_thread.h" | |
| 182 #include "base/threading/thread.h" | |
| 183 #include "base/threading/thread_restrictions.h" | |
| 184 #include "base/tracked_objects.h" | |
| 185 #include "base/values.h" | |
| 186 #include "chrome/browser/browser_process.h" | |
| 187 #include "chrome/browser/chrome_notification_types.h" | |
| 188 #include "chrome/browser/io_thread.h" | |
| 189 #include "chrome/browser/memory_details.h" | |
| 190 #include "chrome/browser/metrics/cloned_install_detector.h" | 16 #include "chrome/browser/metrics/cloned_install_detector.h" |
| 191 #include "chrome/browser/metrics/compression_utils.h" | |
| 192 #include "chrome/browser/metrics/machine_id_provider.h" | 17 #include "chrome/browser/metrics/machine_id_provider.h" |
| 193 #include "chrome/browser/metrics/metrics_log.h" | |
| 194 #include "chrome/browser/metrics/metrics_log_serializer.h" | |
| 195 #include "chrome/browser/metrics/metrics_reporting_scheduler.h" | |
| 196 #include "chrome/browser/metrics/time_ticks_experiment_win.h" | |
| 197 #include "chrome/browser/metrics/tracking_synchronizer.h" | |
| 198 #include "chrome/common/metrics/variations/variations_util.h" | |
| 199 #include "chrome/browser/net/http_pipelining_compatibility_client.h" | |
| 200 #include "chrome/browser/net/network_stats.h" | |
| 201 #include "chrome/browser/omnibox/omnibox_log.h" | |
| 202 #include "chrome/browser/ui/browser_list.h" | |
| 203 #include "chrome/browser/ui/browser_otr_state.h" | |
| 204 #include "chrome/browser/ui/search/search_tab_helper.h" | |
| 205 #include "chrome/common/chrome_constants.h" | |
| 206 #include "chrome/common/chrome_result_codes.h" | |
| 207 #include "chrome/common/chrome_switches.h" | 18 #include "chrome/common/chrome_switches.h" |
| 208 #include "chrome/common/crash_keys.h" | |
| 209 #include "chrome/common/metrics/caching_permuted_entropy_provider.h" | 19 #include "chrome/common/metrics/caching_permuted_entropy_provider.h" |
| 210 #include "chrome/common/metrics/metrics_log_manager.h" | |
| 211 #include "chrome/common/net/test_server_locations.h" | |
| 212 #include "chrome/common/pref_names.h" | 20 #include "chrome/common/pref_names.h" |
| 213 #include "chrome/common/render_messages.h" | |
| 214 #include "components/variations/entropy_provider.h" | |
| 215 #include "components/variations/metrics_util.h" | |
| 216 #include "content/public/browser/child_process_data.h" | |
| 217 #include "content/public/browser/histogram_fetcher.h" | |
| 218 #include "content/public/browser/load_notification_details.h" | |
| 219 #include "content/public/browser/notification_service.h" | |
| 220 #include "content/public/browser/plugin_service.h" | |
| 221 #include "content/public/browser/render_process_host.h" | |
| 222 #include "content/public/browser/user_metrics.h" | |
| 223 #include "content/public/browser/web_contents.h" | |
| 224 #include "content/public/common/process_type.h" | |
| 225 #include "content/public/common/webplugininfo.h" | |
| 226 #include "extensions/browser/process_map.h" | |
| 227 #include "net/base/load_flags.h" | |
| 228 #include "net/url_request/url_fetcher.h" | |
| 229 | 21 |
| 230 // TODO(port): port browser_distribution.h. | 22 namespace metrics { |
| 231 #if !defined(OS_POSIX) | |
| 232 #include "chrome/installer/util/browser_distribution.h" | |
| 233 #endif | |
| 234 | |
| 235 #if defined(OS_CHROMEOS) | |
| 236 #include "chrome/browser/chromeos/external_metrics.h" | |
| 237 #include "chrome/browser/chromeos/settings/cros_settings.h" | |
| 238 #include "chromeos/system/statistics_provider.h" | |
| 239 #endif | |
| 240 | |
| 241 #if defined(OS_WIN) | |
| 242 #include <windows.h> // Needed for STATUS_* codes | |
| 243 #include "base/win/registry.h" | |
| 244 #endif | |
| 245 | |
| 246 #if !defined(OS_ANDROID) | |
| 247 #include "chrome/browser/service_process/service_process_control.h" | |
| 248 #endif | |
| 249 | |
| 250 using base::Time; | |
| 251 using content::BrowserThread; | |
| 252 using content::ChildProcessData; | |
| 253 using content::LoadNotificationDetails; | |
| 254 using content::PluginService; | |
| 255 | 23 |
| 256 namespace { | 24 namespace { |
| 257 | 25 |
| 258 // Check to see that we're being called on only one thread. | |
| 259 bool IsSingleThreaded() { | |
| 260 static base::PlatformThreadId thread_id = 0; | |
| 261 if (!thread_id) | |
| 262 thread_id = base::PlatformThread::CurrentId(); | |
| 263 return base::PlatformThread::CurrentId() == thread_id; | |
| 264 } | |
| 265 | |
| 266 // The delay, in seconds, after starting recording before doing expensive | |
| 267 // initialization work. | |
| 268 #if defined(OS_ANDROID) || defined(OS_IOS) | |
| 269 // On mobile devices, a significant portion of sessions last less than a minute. | |
| 270 // Use a shorter timer on these platforms to avoid losing data. | |
| 271 // TODO(dfalcantara): To avoid delaying startup, tighten up initialization so | |
| 272 // that it occurs after the user gets their initial page. | |
| 273 const int kInitializationDelaySeconds = 5; | |
| 274 #else | |
| 275 const int kInitializationDelaySeconds = 30; | |
| 276 #endif | |
| 277 | |
| 278 // This specifies the amount of time to wait for all renderers to send their | |
| 279 // data. | |
| 280 const int kMaxHistogramGatheringWaitDuration = 60000; // 60 seconds. | |
| 281 | |
| 282 // The maximum number of events in a log uploaded to the UMA server. | |
| 283 const int kEventLimit = 2400; | |
| 284 | |
| 285 // If an upload fails, and the transmission was over this byte count, then we | |
| 286 // will discard the log, and not try to retransmit it. We also don't persist | |
| 287 // the log to the prefs for transmission during the next chrome session if this | |
| 288 // limit is exceeded. | |
| 289 const size_t kUploadLogAvoidRetransmitSize = 50000; | |
| 290 | |
| 291 // Interval, in minutes, between state saves. | |
| 292 const int kSaveStateIntervalMinutes = 5; | |
| 293 | |
| 294 enum ResponseStatus { | |
| 295 UNKNOWN_FAILURE, | |
| 296 SUCCESS, | |
| 297 BAD_REQUEST, // Invalid syntax or log too large. | |
| 298 NO_RESPONSE, | |
| 299 NUM_RESPONSE_STATUSES | |
| 300 }; | |
| 301 | |
| 302 ResponseStatus ResponseCodeToStatus(int response_code) { | |
| 303 switch (response_code) { | |
| 304 case 200: | |
| 305 return SUCCESS; | |
| 306 case 400: | |
| 307 return BAD_REQUEST; | |
| 308 case net::URLFetcher::RESPONSE_CODE_INVALID: | |
| 309 return NO_RESPONSE; | |
| 310 default: | |
| 311 return UNKNOWN_FAILURE; | |
| 312 } | |
| 313 } | |
| 314 | |
| 315 // The argument used to generate a non-identifying entropy source. We want no | 26 // The argument used to generate a non-identifying entropy source. We want no |
| 316 // more than 13 bits of entropy, so use this max to return a number in the range | 27 // more than 13 bits of entropy, so use this max to return a number in the range |
| 317 // [0, 7999] as the entropy source (12.97 bits of entropy). | 28 // [0, 7999] as the entropy source (12.97 bits of entropy). |
| 318 const int kMaxLowEntropySize = 8000; | 29 const int kMaxLowEntropySize = 8000; |
| 319 | 30 |
| 320 // Default prefs value for prefs::kMetricsLowEntropySource to indicate that the | 31 // Default prefs value for prefs::kMetricsLowEntropySource to indicate that the |
| 321 // value has not yet been set. | 32 // value has not yet been set. |
| 322 const int kLowEntropySourceNotSet = -1; | 33 const int kLowEntropySourceNotSet = -1; |
| 323 | 34 |
| 324 // Generates a new non-identifying entropy source used to seed persistent | 35 // Generates a new non-identifying entropy source used to seed persistent |
| 325 // activities. | 36 // activities. |
| 326 int GenerateLowEntropySource() { | 37 int GenerateLowEntropySource() { |
| 327 return base::RandInt(0, kMaxLowEntropySize - 1); | 38 return base::RandInt(0, kMaxLowEntropySize - 1); |
| 328 } | 39 } |
| 329 | 40 |
| 330 // Converts an exit code into something that can be inserted into our | 41 bool IsMetricsReportingEnabledImpl(PrefService* local_state) { |
|
Ilya Sherman
2014/05/02 05:12:38
nit: Docs.
Alexei Svitkine (slow)
2014/05/02 15:21:46
Removed this Impl method and inlined it into the m
| |
| 331 // histograms (which expect non-negative numbers less than MAX_INT). | 42 // If the user permits metrics reporting with the checkbox in the |
| 332 int MapCrashExitCodeForHistogram(int exit_code) { | 43 // prefs, we turn on recording. We disable metrics completely for |
| 333 #if defined(OS_WIN) | 44 // non-official builds. This can be forced with a flag. |
| 334 // Since |abs(STATUS_GUARD_PAGE_VIOLATION) == MAX_INT| it causes problems in | 45 const CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| 335 // histograms.cc. Solve this by remapping it to a smaller value, which | 46 if (command_line->HasSwitch(switches::kEnableMetricsReportingForTesting)) |
| 336 // hopefully doesn't conflict with other codes. | 47 return true; |
| 337 if (exit_code == STATUS_GUARD_PAGE_VIOLATION) | |
| 338 return 0x1FCF7EC3; // Randomly picked number. | |
| 339 #endif | |
| 340 | 48 |
| 341 return std::abs(exit_code); | 49 bool enabled = false; |
| 342 } | 50 // The debug build doesn't send UMA logs when FieldTrials are forced. |
|
Ilya Sherman
2014/05/02 05:12:38
I know that you copied this line verbatim, but wha
Alexei Svitkine (slow)
2014/05/02 15:21:46
Done. Yeah, the comment was out of date.
| |
| 51 if (command_line->HasSwitch(switches::kForceFieldTrials)) | |
| 52 return false; | |
| 343 | 53 |
| 344 void MarkAppCleanShutdownAndCommit() { | 54 #if defined(GOOGLE_CHROME_BUILD) |
| 345 PrefService* pref = g_browser_process->local_state(); | 55 #if defined(OS_CHROMEOS) |
| 346 pref->SetBoolean(prefs::kStabilityExitedCleanly, true); | 56 chromeos::CrosSettings::Get()->GetBoolean(chromeos::kStatsReportingPref, |
| 347 pref->SetInteger(prefs::kStabilityExecutionPhase, | 57 &enabled); |
| 348 MetricsService::SHUTDOWN_COMPLETE); | 58 #else |
| 349 // Start writing right away (write happens on a different thread). | 59 enabled = local_state->GetBoolean(prefs::kMetricsReportingEnabled); |
| 350 pref->CommitPendingWrite(); | 60 #endif // #if defined(OS_CHROMEOS) |
| 61 #endif // defined(GOOGLE_CHROME_BUILD) | |
| 62 return enabled; | |
| 351 } | 63 } |
| 352 | 64 |
| 353 } // namespace | 65 } // namespace |
| 354 | 66 |
| 355 | 67 MetricsStateManager::MetricsStateManager(PrefService* local_state) |
| 356 SyntheticTrialGroup::SyntheticTrialGroup(uint32 trial, uint32 group) { | 68 : local_state_(local_state), |
| 357 id.name = trial; | 69 metrics_reporting_enabled_(IsMetricsReportingEnabledImpl(local_state)), |
| 358 id.group = group; | 70 low_entropy_source_(kLowEntropySourceNotSet), |
| 71 entropy_source_returned_(LAST_ENTROPY_NONE) { | |
| 72 ResetMetricsIDsIfNecessary(); | |
| 73 if (metrics_reporting_enabled_) | |
| 74 ForceClientIdCreation(); | |
| 359 } | 75 } |
| 360 | 76 |
| 361 SyntheticTrialGroup::~SyntheticTrialGroup() { | 77 MetricsStateManager::~MetricsStateManager() { |
| 362 } | 78 } |
| 363 | 79 |
| 364 // static | 80 bool MetricsStateManager::IsMetricsReportingEnabled() { |
| 365 MetricsService::ShutdownCleanliness MetricsService::clean_shutdown_status_ = | 81 return metrics_reporting_enabled_; |
| 366 MetricsService::CLEANLY_SHUTDOWN; | |
| 367 | |
| 368 MetricsService::ExecutionPhase MetricsService::execution_phase_ = | |
| 369 MetricsService::UNINITIALIZED_PHASE; | |
| 370 | |
| 371 // This is used to quickly log stats from child process related notifications in | |
| 372 // MetricsService::child_stats_buffer_. The buffer's contents are transferred | |
| 373 // out when Local State is periodically saved. The information is then | |
| 374 // reported to the UMA server on next launch. | |
| 375 struct MetricsService::ChildProcessStats { | |
| 376 public: | |
| 377 explicit ChildProcessStats(int process_type) | |
| 378 : process_launches(0), | |
| 379 process_crashes(0), | |
| 380 instances(0), | |
| 381 loading_errors(0), | |
| 382 process_type(process_type) {} | |
| 383 | |
| 384 // This constructor is only used by the map to return some default value for | |
| 385 // an index for which no value has been assigned. | |
| 386 ChildProcessStats() | |
| 387 : process_launches(0), | |
| 388 process_crashes(0), | |
| 389 instances(0), | |
| 390 loading_errors(0), | |
| 391 process_type(content::PROCESS_TYPE_UNKNOWN) {} | |
| 392 | |
| 393 // The number of times that the given child process has been launched | |
| 394 int process_launches; | |
| 395 | |
| 396 // The number of times that the given child process has crashed | |
| 397 int process_crashes; | |
| 398 | |
| 399 // The number of instances of this child process that have been created. | |
| 400 // An instance is a DOM object rendered by this child process during a page | |
| 401 // load. | |
| 402 int instances; | |
| 403 | |
| 404 // The number of times there was an error loading an instance of this child | |
| 405 // process. | |
| 406 int loading_errors; | |
| 407 | |
| 408 int process_type; | |
| 409 }; | |
| 410 | |
| 411 // Handles asynchronous fetching of memory details. | |
| 412 // Will run the provided task after finished. | |
| 413 class MetricsMemoryDetails : public MemoryDetails { | |
| 414 public: | |
| 415 explicit MetricsMemoryDetails(const base::Closure& callback) | |
| 416 : callback_(callback) {} | |
| 417 | |
| 418 virtual void OnDetailsAvailable() OVERRIDE { | |
| 419 base::MessageLoop::current()->PostTask(FROM_HERE, callback_); | |
| 420 } | |
| 421 | |
| 422 private: | |
| 423 virtual ~MetricsMemoryDetails() {} | |
| 424 | |
| 425 base::Closure callback_; | |
| 426 DISALLOW_COPY_AND_ASSIGN(MetricsMemoryDetails); | |
| 427 }; | |
| 428 | |
| 429 // static | |
| 430 void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) { | |
| 431 DCHECK(IsSingleThreaded()); | |
| 432 registry->RegisterBooleanPref(prefs::kMetricsResetIds, false); | |
| 433 registry->RegisterStringPref(prefs::kMetricsClientID, std::string()); | |
| 434 registry->RegisterInt64Pref(prefs::kMetricsReportingEnabledTimestamp, 0); | |
| 435 registry->RegisterIntegerPref(prefs::kMetricsLowEntropySource, | |
| 436 kLowEntropySourceNotSet); | |
| 437 registry->RegisterInt64Pref(prefs::kStabilityLaunchTimeSec, 0); | |
| 438 registry->RegisterInt64Pref(prefs::kStabilityLastTimestampSec, 0); | |
| 439 registry->RegisterStringPref(prefs::kStabilityStatsVersion, std::string()); | |
| 440 registry->RegisterInt64Pref(prefs::kStabilityStatsBuildTime, 0); | |
| 441 registry->RegisterBooleanPref(prefs::kStabilityExitedCleanly, true); | |
| 442 registry->RegisterIntegerPref(prefs::kStabilityExecutionPhase, | |
| 443 UNINITIALIZED_PHASE); | |
| 444 registry->RegisterBooleanPref(prefs::kStabilitySessionEndCompleted, true); | |
| 445 registry->RegisterIntegerPref(prefs::kMetricsSessionID, -1); | |
| 446 registry->RegisterIntegerPref(prefs::kStabilityLaunchCount, 0); | |
| 447 registry->RegisterIntegerPref(prefs::kStabilityCrashCount, 0); | |
| 448 registry->RegisterIntegerPref(prefs::kStabilityIncompleteSessionEndCount, 0); | |
| 449 registry->RegisterIntegerPref(prefs::kStabilityPageLoadCount, 0); | |
| 450 registry->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0); | |
| 451 registry->RegisterIntegerPref(prefs::kStabilityExtensionRendererCrashCount, | |
| 452 0); | |
| 453 registry->RegisterIntegerPref(prefs::kStabilityRendererHangCount, 0); | |
| 454 registry->RegisterIntegerPref(prefs::kStabilityChildProcessCrashCount, 0); | |
| 455 registry->RegisterIntegerPref(prefs::kStabilityBreakpadRegistrationFail, 0); | |
| 456 registry->RegisterIntegerPref(prefs::kStabilityBreakpadRegistrationSuccess, | |
| 457 0); | |
| 458 registry->RegisterIntegerPref(prefs::kStabilityDebuggerPresent, 0); | |
| 459 registry->RegisterIntegerPref(prefs::kStabilityDebuggerNotPresent, 0); | |
| 460 #if defined(OS_CHROMEOS) | |
| 461 registry->RegisterIntegerPref(prefs::kStabilityOtherUserCrashCount, 0); | |
| 462 registry->RegisterIntegerPref(prefs::kStabilityKernelCrashCount, 0); | |
| 463 registry->RegisterIntegerPref(prefs::kStabilitySystemUncleanShutdownCount, 0); | |
| 464 #endif // OS_CHROMEOS | |
| 465 | |
| 466 registry->RegisterStringPref(prefs::kStabilitySavedSystemProfile, | |
| 467 std::string()); | |
| 468 registry->RegisterStringPref(prefs::kStabilitySavedSystemProfileHash, | |
| 469 std::string()); | |
| 470 | |
| 471 registry->RegisterListPref(prefs::kMetricsInitialLogs); | |
| 472 registry->RegisterListPref(prefs::kMetricsOngoingLogs); | |
| 473 | |
| 474 registry->RegisterInt64Pref(prefs::kInstallDate, 0); | |
| 475 registry->RegisterInt64Pref(prefs::kUninstallMetricsPageLoadCount, 0); | |
| 476 registry->RegisterInt64Pref(prefs::kUninstallLaunchCount, 0); | |
| 477 registry->RegisterInt64Pref(prefs::kUninstallMetricsUptimeSec, 0); | |
| 478 registry->RegisterInt64Pref(prefs::kUninstallLastLaunchTimeSec, 0); | |
| 479 registry->RegisterInt64Pref(prefs::kUninstallLastObservedRunTimeSec, 0); | |
| 480 | |
| 481 // TODO(asvitkine): Remove these once a couple of releases have passed. | |
| 482 // http://crbug.com/357704 | |
| 483 registry->RegisterStringPref(prefs::kMetricsOldClientID, std::string()); | |
| 484 registry->RegisterIntegerPref(prefs::kMetricsOldLowEntropySource, 0); | |
| 485 | |
| 486 #if defined(OS_ANDROID) | |
| 487 RegisterPrefsAndroid(registry); | |
| 488 #endif // defined(OS_ANDROID) | |
| 489 } | 82 } |
| 490 | 83 |
| 491 MetricsService::MetricsService() | 84 void MetricsStateManager::ForceClientIdCreation() { |
| 492 : metrics_ids_reset_check_performed_(false), | 85 if (!client_id_.empty()) |
| 493 recording_active_(false), | 86 return; |
| 494 reporting_active_(false), | |
| 495 test_mode_active_(false), | |
| 496 state_(INITIALIZED), | |
| 497 has_initial_stability_log_(false), | |
| 498 low_entropy_source_(kLowEntropySourceNotSet), | |
| 499 idle_since_last_transmission_(false), | |
| 500 session_id_(-1), | |
| 501 next_window_id_(0), | |
| 502 self_ptr_factory_(this), | |
| 503 state_saver_factory_(this), | |
| 504 waiting_for_asynchronous_reporting_step_(false), | |
| 505 num_async_histogram_fetches_in_progress_(0), | |
| 506 entropy_source_returned_(LAST_ENTROPY_NONE) { | |
| 507 DCHECK(IsSingleThreaded()); | |
| 508 | 87 |
| 509 log_manager_.set_log_serializer(new MetricsLogSerializer); | 88 client_id_ = local_state_->GetString(prefs::kMetricsClientID); |
| 510 log_manager_.set_max_ongoing_log_store_size(kUploadLogAvoidRetransmitSize); | 89 if (!client_id_.empty()) |
| 90 return; | |
| 511 | 91 |
| 512 BrowserChildProcessObserver::Add(this); | 92 client_id_ = base::GenerateGUID(); |
| 93 local_state_->SetString(prefs::kMetricsClientID, client_id_); | |
| 94 | |
| 95 if (local_state_->GetString(prefs::kMetricsOldClientID).empty()) { | |
| 96 // Record the timestamp of when the user opted in to UMA. | |
| 97 local_state_->SetInt64(prefs::kMetricsReportingEnabledTimestamp, | |
| 98 base::Time::Now().ToTimeT()); | |
| 99 } else { | |
| 100 UMA_HISTOGRAM_BOOLEAN("UMA.ClientIdMigrated", true); | |
| 101 } | |
| 102 local_state_->ClearPref(prefs::kMetricsOldClientID); | |
| 513 } | 103 } |
| 514 | 104 |
| 515 MetricsService::~MetricsService() { | 105 void MetricsStateManager::CheckForClonedInstall() { |
| 516 DisableRecording(); | 106 DCHECK(!cloned_install_detector_); |
| 517 | 107 |
| 518 BrowserChildProcessObserver::Remove(this); | 108 MachineIdProvider* provider = MachineIdProvider::CreateInstance(); |
| 519 } | 109 if (!provider) |
| 110 return; | |
| 520 | 111 |
| 521 void MetricsService::InitializeMetricsRecordingState( | 112 cloned_install_detector_.reset(new ClonedInstallDetector(provider)); |
| 522 ReportingState reporting_state) { | 113 cloned_install_detector_->CheckForClonedInstall(local_state_); |
| 523 InitializeMetricsState(reporting_state); | |
| 524 | |
| 525 base::Closure callback = base::Bind(&MetricsService::StartScheduledUpload, | |
| 526 self_ptr_factory_.GetWeakPtr()); | |
| 527 scheduler_.reset(new MetricsReportingScheduler(callback)); | |
| 528 } | |
| 529 | |
| 530 void MetricsService::Start() { | |
| 531 HandleIdleSinceLastTransmission(false); | |
| 532 EnableRecording(); | |
| 533 EnableReporting(); | |
| 534 } | |
| 535 | |
| 536 void MetricsService::StartRecordingForTests() { | |
| 537 test_mode_active_ = true; | |
| 538 EnableRecording(); | |
| 539 DisableReporting(); | |
| 540 } | |
| 541 | |
| 542 void MetricsService::Stop() { | |
| 543 HandleIdleSinceLastTransmission(false); | |
| 544 DisableReporting(); | |
| 545 DisableRecording(); | |
| 546 } | |
| 547 | |
| 548 void MetricsService::EnableReporting() { | |
| 549 if (reporting_active_) | |
| 550 return; | |
| 551 reporting_active_ = true; | |
| 552 StartSchedulerIfNecessary(); | |
| 553 } | |
| 554 | |
| 555 void MetricsService::DisableReporting() { | |
| 556 reporting_active_ = false; | |
| 557 } | |
| 558 | |
| 559 std::string MetricsService::GetClientId() { | |
| 560 return client_id_; | |
| 561 } | 114 } |
| 562 | 115 |
| 563 scoped_ptr<const base::FieldTrial::EntropyProvider> | 116 scoped_ptr<const base::FieldTrial::EntropyProvider> |
| 564 MetricsService::CreateEntropyProvider(ReportingState reporting_state) { | 117 MetricsStateManager::CreateEntropyProvider() { |
| 565 // For metrics reporting-enabled users, we combine the client ID and low | 118 // For metrics reporting-enabled users, we combine the client ID and low |
| 566 // entropy source to get the final entropy source. Otherwise, only use the low | 119 // entropy source to get the final entropy source. Otherwise, only use the low |
| 567 // entropy source. | 120 // entropy source. |
| 568 // This has two useful properties: | 121 // This has two useful properties: |
| 569 // 1) It makes the entropy source less identifiable for parties that do not | 122 // 1) It makes the entropy source less identifiable for parties that do not |
| 570 // know the low entropy source. | 123 // know the low entropy source. |
| 571 // 2) It makes the final entropy source resettable. | 124 // 2) It makes the final entropy source resettable. |
| 572 const int low_entropy_source_value = GetLowEntropySource(); | 125 const int low_entropy_source_value = GetLowEntropySource(); |
| 573 UMA_HISTOGRAM_SPARSE_SLOWLY("UMA.LowEntropySourceValue", | 126 UMA_HISTOGRAM_SPARSE_SLOWLY("UMA.LowEntropySourceValue", |
| 574 low_entropy_source_value); | 127 low_entropy_source_value); |
| 575 if (reporting_state == REPORTING_ENABLED) { | 128 if (metrics_reporting_enabled_) { |
| 576 if (entropy_source_returned_ == LAST_ENTROPY_NONE) | 129 if (entropy_source_returned_ == LAST_ENTROPY_NONE) |
| 577 entropy_source_returned_ = LAST_ENTROPY_HIGH; | 130 entropy_source_returned_ = LAST_ENTROPY_HIGH; |
| 578 DCHECK_EQ(LAST_ENTROPY_HIGH, entropy_source_returned_); | 131 DCHECK_EQ(LAST_ENTROPY_HIGH, entropy_source_returned_); |
| 579 const std::string high_entropy_source = | 132 const std::string high_entropy_source = |
| 580 client_id_ + base::IntToString(low_entropy_source_value); | 133 client_id_ + base::IntToString(low_entropy_source_value); |
| 581 return scoped_ptr<const base::FieldTrial::EntropyProvider>( | 134 return scoped_ptr<const base::FieldTrial::EntropyProvider>( |
| 582 new metrics::SHA1EntropyProvider(high_entropy_source)); | 135 new SHA1EntropyProvider(high_entropy_source)); |
| 583 } | 136 } |
| 584 | 137 |
| 585 if (entropy_source_returned_ == LAST_ENTROPY_NONE) | 138 if (entropy_source_returned_ == LAST_ENTROPY_NONE) |
| 586 entropy_source_returned_ = LAST_ENTROPY_LOW; | 139 entropy_source_returned_ = LAST_ENTROPY_LOW; |
| 587 DCHECK_EQ(LAST_ENTROPY_LOW, entropy_source_returned_); | 140 DCHECK_EQ(LAST_ENTROPY_LOW, entropy_source_returned_); |
| 588 | 141 |
| 589 #if defined(OS_ANDROID) || defined(OS_IOS) | 142 #if defined(OS_ANDROID) || defined(OS_IOS) |
| 590 return scoped_ptr<const base::FieldTrial::EntropyProvider>( | 143 return scoped_ptr<const base::FieldTrial::EntropyProvider>( |
| 591 new metrics::CachingPermutedEntropyProvider( | 144 new CachingPermutedEntropyProvider(local_state_, |
| 592 g_browser_process->local_state(), | 145 low_entropy_source_value, |
| 593 low_entropy_source_value, | 146 kMaxLowEntropySize)); |
| 594 kMaxLowEntropySize)); | |
| 595 #else | 147 #else |
| 596 return scoped_ptr<const base::FieldTrial::EntropyProvider>( | 148 return scoped_ptr<const base::FieldTrial::EntropyProvider>( |
| 597 new metrics::PermutedEntropyProvider(low_entropy_source_value, | 149 new PermutedEntropyProvider(low_entropy_source_value, |
| 598 kMaxLowEntropySize)); | 150 kMaxLowEntropySize)); |
| 599 #endif | 151 #endif |
| 600 } | 152 } |
| 601 | 153 |
| 602 void MetricsService::ForceClientIdCreation() { | 154 // static |
| 603 if (!client_id_.empty()) | 155 void MetricsStateManager::RegisterPrefs(PrefRegistrySimple* registry) { |
| 604 return; | 156 registry->RegisterBooleanPref(prefs::kMetricsResetIds, false); |
| 157 registry->RegisterStringPref(prefs::kMetricsClientID, std::string()); | |
| 158 registry->RegisterInt64Pref(prefs::kMetricsReportingEnabledTimestamp, 0); | |
| 159 registry->RegisterIntegerPref(prefs::kMetricsLowEntropySource, | |
| 160 kLowEntropySourceNotSet); | |
| 605 | 161 |
| 606 ResetMetricsIDsIfNecessary(); | 162 ClonedInstallDetector::RegisterPrefs(registry); |
| 163 CachingPermutedEntropyProvider::RegisterPrefs(registry); | |
| 607 | 164 |
| 608 PrefService* pref = g_browser_process->local_state(); | 165 // TODO(asvitkine): Remove these once a couple of releases have passed. |
| 609 client_id_ = pref->GetString(prefs::kMetricsClientID); | 166 // http://crbug.com/357704 |
| 610 if (!client_id_.empty()) | 167 registry->RegisterStringPref(prefs::kMetricsOldClientID, std::string()); |
| 611 return; | 168 registry->RegisterIntegerPref(prefs::kMetricsOldLowEntropySource, 0); |
| 612 | |
| 613 client_id_ = GenerateClientID(); | |
| 614 pref->SetString(prefs::kMetricsClientID, client_id_); | |
| 615 | |
| 616 if (pref->GetString(prefs::kMetricsOldClientID).empty()) { | |
| 617 // Record the timestamp of when the user opted in to UMA. | |
| 618 pref->SetInt64(prefs::kMetricsReportingEnabledTimestamp, | |
| 619 Time::Now().ToTimeT()); | |
| 620 } else { | |
| 621 UMA_HISTOGRAM_BOOLEAN("UMA.ClientIdMigrated", true); | |
| 622 } | |
| 623 pref->ClearPref(prefs::kMetricsOldClientID); | |
| 624 } | 169 } |
| 625 | 170 |
| 626 void MetricsService::EnableRecording() { | 171 int MetricsStateManager::GetLowEntropySource() { |
| 627 DCHECK(IsSingleThreaded()); | |
| 628 | |
| 629 if (recording_active_) | |
| 630 return; | |
| 631 recording_active_ = true; | |
| 632 | |
| 633 ForceClientIdCreation(); | |
| 634 crash_keys::SetClientID(client_id_); | |
| 635 if (!log_manager_.current_log()) | |
| 636 OpenNewLog(); | |
| 637 | |
| 638 SetUpNotifications(®istrar_, this); | |
| 639 base::RemoveActionCallback(action_callback_); | |
| 640 action_callback_ = base::Bind(&MetricsService::OnUserAction, | |
| 641 base::Unretained(this)); | |
| 642 base::AddActionCallback(action_callback_); | |
| 643 } | |
| 644 | |
| 645 void MetricsService::DisableRecording() { | |
| 646 DCHECK(IsSingleThreaded()); | |
| 647 | |
| 648 if (!recording_active_) | |
| 649 return; | |
| 650 recording_active_ = false; | |
| 651 | |
| 652 base::RemoveActionCallback(action_callback_); | |
| 653 registrar_.RemoveAll(); | |
| 654 PushPendingLogsToPersistentStorage(); | |
| 655 DCHECK(!log_manager_.has_staged_log()); | |
| 656 } | |
| 657 | |
| 658 bool MetricsService::recording_active() const { | |
| 659 DCHECK(IsSingleThreaded()); | |
| 660 return recording_active_; | |
| 661 } | |
| 662 | |
| 663 bool MetricsService::reporting_active() const { | |
| 664 DCHECK(IsSingleThreaded()); | |
| 665 return reporting_active_; | |
| 666 } | |
| 667 | |
| 668 // static | |
| 669 void MetricsService::SetUpNotifications( | |
| 670 content::NotificationRegistrar* registrar, | |
| 671 content::NotificationObserver* observer) { | |
| 672 registrar->Add(observer, chrome::NOTIFICATION_BROWSER_OPENED, | |
| 673 content::NotificationService::AllBrowserContextsAndSources()); | |
| 674 registrar->Add(observer, chrome::NOTIFICATION_BROWSER_CLOSED, | |
| 675 content::NotificationService::AllSources()); | |
| 676 registrar->Add(observer, chrome::NOTIFICATION_TAB_PARENTED, | |
| 677 content::NotificationService::AllSources()); | |
| 678 registrar->Add(observer, chrome::NOTIFICATION_TAB_CLOSING, | |
| 679 content::NotificationService::AllSources()); | |
| 680 registrar->Add(observer, content::NOTIFICATION_LOAD_START, | |
| 681 content::NotificationService::AllSources()); | |
| 682 registrar->Add(observer, content::NOTIFICATION_LOAD_STOP, | |
| 683 content::NotificationService::AllSources()); | |
| 684 registrar->Add(observer, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, | |
| 685 content::NotificationService::AllSources()); | |
| 686 registrar->Add(observer, content::NOTIFICATION_RENDER_WIDGET_HOST_HANG, | |
| 687 content::NotificationService::AllSources()); | |
| 688 registrar->Add(observer, chrome::NOTIFICATION_OMNIBOX_OPENED_URL, | |
| 689 content::NotificationService::AllSources()); | |
| 690 } | |
| 691 | |
| 692 void MetricsService::BrowserChildProcessHostConnected( | |
| 693 const content::ChildProcessData& data) { | |
| 694 GetChildProcessStats(data).process_launches++; | |
| 695 } | |
| 696 | |
| 697 void MetricsService::BrowserChildProcessCrashed( | |
| 698 const content::ChildProcessData& data) { | |
| 699 GetChildProcessStats(data).process_crashes++; | |
| 700 // Exclude plugin crashes from the count below because we report them via | |
| 701 // a separate UMA metric. | |
| 702 if (!IsPluginProcess(data.process_type)) | |
| 703 IncrementPrefValue(prefs::kStabilityChildProcessCrashCount); | |
| 704 } | |
| 705 | |
| 706 void MetricsService::BrowserChildProcessInstanceCreated( | |
| 707 const content::ChildProcessData& data) { | |
| 708 GetChildProcessStats(data).instances++; | |
| 709 } | |
| 710 | |
| 711 void MetricsService::Observe(int type, | |
| 712 const content::NotificationSource& source, | |
| 713 const content::NotificationDetails& details) { | |
| 714 DCHECK(log_manager_.current_log()); | |
| 715 DCHECK(IsSingleThreaded()); | |
| 716 | |
| 717 // Check for notifications related to core stability metrics, or that are | |
| 718 // just triggers to end idle mode. Anything else should be added in the later | |
| 719 // switch statement, where they take effect only if general metrics should be | |
| 720 // logged. | |
| 721 bool handled = false; | |
| 722 switch (type) { | |
| 723 case chrome::NOTIFICATION_BROWSER_OPENED: | |
| 724 case chrome::NOTIFICATION_BROWSER_CLOSED: | |
| 725 case chrome::NOTIFICATION_TAB_PARENTED: | |
| 726 case chrome::NOTIFICATION_TAB_CLOSING: | |
| 727 case content::NOTIFICATION_LOAD_STOP: | |
| 728 // These notifications are used only to break out of idle mode. | |
| 729 handled = true; | |
| 730 break; | |
| 731 | |
| 732 case content::NOTIFICATION_LOAD_START: { | |
| 733 content::NavigationController* controller = | |
| 734 content::Source<content::NavigationController>(source).ptr(); | |
| 735 content::WebContents* web_contents = controller->GetWebContents(); | |
| 736 LogLoadStarted(web_contents); | |
| 737 handled = true; | |
| 738 break; | |
| 739 } | |
| 740 | |
| 741 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { | |
| 742 content::RenderProcessHost::RendererClosedDetails* process_details = | |
| 743 content::Details< | |
| 744 content::RenderProcessHost::RendererClosedDetails>( | |
| 745 details).ptr(); | |
| 746 content::RenderProcessHost* host = | |
| 747 content::Source<content::RenderProcessHost>(source).ptr(); | |
| 748 LogRendererCrash( | |
| 749 host, process_details->status, process_details->exit_code); | |
| 750 handled = true; | |
| 751 break; | |
| 752 } | |
| 753 | |
| 754 case content::NOTIFICATION_RENDER_WIDGET_HOST_HANG: | |
| 755 LogRendererHang(); | |
| 756 handled = true; | |
| 757 break; | |
| 758 | |
| 759 default: | |
| 760 // Everything else is handled after the early return check below. | |
| 761 break; | |
| 762 } | |
| 763 | |
| 764 // If it wasn't one of the stability-related notifications, and event | |
| 765 // logging isn't suppressed, handle it. | |
| 766 if (!handled && ShouldLogEvents()) { | |
| 767 switch (type) { | |
| 768 case chrome::NOTIFICATION_OMNIBOX_OPENED_URL: { | |
| 769 MetricsLog* current_log = | |
| 770 static_cast<MetricsLog*>(log_manager_.current_log()); | |
| 771 DCHECK(current_log); | |
| 772 current_log->RecordOmniboxOpenedURL( | |
| 773 *content::Details<OmniboxLog>(details).ptr()); | |
| 774 break; | |
| 775 } | |
| 776 | |
| 777 default: | |
| 778 NOTREACHED(); | |
| 779 break; | |
| 780 } | |
| 781 } | |
| 782 | |
| 783 HandleIdleSinceLastTransmission(false); | |
| 784 } | |
| 785 | |
| 786 void MetricsService::HandleIdleSinceLastTransmission(bool in_idle) { | |
| 787 // If there wasn't a lot of action, maybe the computer was asleep, in which | |
| 788 // case, the log transmissions should have stopped. Here we start them up | |
| 789 // again. | |
| 790 if (!in_idle && idle_since_last_transmission_) | |
| 791 StartSchedulerIfNecessary(); | |
| 792 idle_since_last_transmission_ = in_idle; | |
| 793 } | |
| 794 | |
| 795 void MetricsService::RecordStartOfSessionEnd() { | |
| 796 LogCleanShutdown(); | |
| 797 RecordBooleanPrefValue(prefs::kStabilitySessionEndCompleted, false); | |
| 798 } | |
| 799 | |
| 800 void MetricsService::RecordCompletedSessionEnd() { | |
| 801 LogCleanShutdown(); | |
| 802 RecordBooleanPrefValue(prefs::kStabilitySessionEndCompleted, true); | |
| 803 } | |
| 804 | |
| 805 #if defined(OS_ANDROID) || defined(OS_IOS) | |
| 806 void MetricsService::OnAppEnterBackground() { | |
| 807 scheduler_->Stop(); | |
| 808 | |
| 809 MarkAppCleanShutdownAndCommit(); | |
| 810 | |
| 811 // At this point, there's no way of knowing when the process will be | |
| 812 // killed, so this has to be treated similar to a shutdown, closing and | |
| 813 // persisting all logs. Unlinke a shutdown, the state is primed to be ready | |
| 814 // to continue logging and uploading if the process does return. | |
| 815 if (recording_active() && state_ >= SENDING_INITIAL_STABILITY_LOG) { | |
| 816 PushPendingLogsToPersistentStorage(); | |
| 817 // Persisting logs closes the current log, so start recording a new log | |
| 818 // immediately to capture any background work that might be done before the | |
| 819 // process is killed. | |
| 820 OpenNewLog(); | |
| 821 } | |
| 822 } | |
| 823 | |
| 824 void MetricsService::OnAppEnterForeground() { | |
| 825 PrefService* pref = g_browser_process->local_state(); | |
| 826 pref->SetBoolean(prefs::kStabilityExitedCleanly, false); | |
| 827 | |
| 828 StartSchedulerIfNecessary(); | |
| 829 } | |
| 830 #else | |
| 831 void MetricsService::LogNeedForCleanShutdown() { | |
| 832 PrefService* pref = g_browser_process->local_state(); | |
| 833 pref->SetBoolean(prefs::kStabilityExitedCleanly, false); | |
| 834 // Redundant setting to be sure we call for a clean shutdown. | |
| 835 clean_shutdown_status_ = NEED_TO_SHUTDOWN; | |
| 836 } | |
| 837 #endif // defined(OS_ANDROID) || defined(OS_IOS) | |
| 838 | |
| 839 // static | |
| 840 void MetricsService::SetExecutionPhase(ExecutionPhase execution_phase) { | |
| 841 execution_phase_ = execution_phase; | |
| 842 PrefService* pref = g_browser_process->local_state(); | |
| 843 pref->SetInteger(prefs::kStabilityExecutionPhase, execution_phase_); | |
| 844 } | |
| 845 | |
| 846 void MetricsService::RecordBreakpadRegistration(bool success) { | |
| 847 if (!success) | |
| 848 IncrementPrefValue(prefs::kStabilityBreakpadRegistrationFail); | |
| 849 else | |
| 850 IncrementPrefValue(prefs::kStabilityBreakpadRegistrationSuccess); | |
| 851 } | |
| 852 | |
| 853 void MetricsService::RecordBreakpadHasDebugger(bool has_debugger) { | |
| 854 if (!has_debugger) | |
| 855 IncrementPrefValue(prefs::kStabilityDebuggerNotPresent); | |
| 856 else | |
| 857 IncrementPrefValue(prefs::kStabilityDebuggerPresent); | |
| 858 } | |
| 859 | |
| 860 #if defined(OS_WIN) | |
| 861 void MetricsService::CountBrowserCrashDumpAttempts() { | |
| 862 // Open the registry key for iteration. | |
| 863 base::win::RegKey regkey; | |
| 864 if (regkey.Open(HKEY_CURRENT_USER, | |
| 865 chrome::kBrowserCrashDumpAttemptsRegistryPath, | |
| 866 KEY_ALL_ACCESS) != ERROR_SUCCESS) { | |
| 867 return; | |
| 868 } | |
| 869 | |
| 870 // The values we're interested in counting are all prefixed with the version. | |
| 871 base::string16 chrome_version(base::ASCIIToUTF16(chrome::kChromeVersion)); | |
| 872 | |
| 873 // Track a list of values to delete. We don't modify the registry key while | |
| 874 // we're iterating over its values. | |
| 875 typedef std::vector<base::string16> StringVector; | |
| 876 StringVector to_delete; | |
| 877 | |
| 878 // Iterate over the values in the key counting dumps with and without crashes. | |
| 879 // We directly walk the values instead of using RegistryValueIterator in order | |
| 880 // to read all of the values as DWORDS instead of strings. | |
| 881 base::string16 name; | |
| 882 DWORD value = 0; | |
| 883 int dumps_with_crash = 0; | |
| 884 int dumps_with_no_crash = 0; | |
| 885 for (int i = regkey.GetValueCount() - 1; i >= 0; --i) { | |
| 886 if (regkey.GetValueNameAt(i, &name) == ERROR_SUCCESS && | |
| 887 StartsWith(name, chrome_version, false) && | |
| 888 regkey.ReadValueDW(name.c_str(), &value) == ERROR_SUCCESS) { | |
| 889 to_delete.push_back(name); | |
| 890 if (value == 0) | |
| 891 ++dumps_with_no_crash; | |
| 892 else | |
| 893 ++dumps_with_crash; | |
| 894 } | |
| 895 } | |
| 896 | |
| 897 // Delete the registry keys we've just counted. | |
| 898 for (StringVector::iterator i = to_delete.begin(); i != to_delete.end(); ++i) | |
| 899 regkey.DeleteValue(i->c_str()); | |
| 900 | |
| 901 // Capture the histogram samples. | |
| 902 if (dumps_with_crash != 0) | |
| 903 UMA_HISTOGRAM_COUNTS("Chrome.BrowserDumpsWithCrash", dumps_with_crash); | |
| 904 if (dumps_with_no_crash != 0) | |
| 905 UMA_HISTOGRAM_COUNTS("Chrome.BrowserDumpsWithNoCrash", dumps_with_no_crash); | |
| 906 int total_dumps = dumps_with_crash + dumps_with_no_crash; | |
| 907 if (total_dumps != 0) | |
| 908 UMA_HISTOGRAM_COUNTS("Chrome.BrowserCrashDumpAttempts", total_dumps); | |
| 909 } | |
| 910 #endif // defined(OS_WIN) | |
| 911 | |
| 912 //------------------------------------------------------------------------------ | |
| 913 // private methods | |
| 914 //------------------------------------------------------------------------------ | |
| 915 | |
| 916 | |
| 917 //------------------------------------------------------------------------------ | |
| 918 // Initialization methods | |
| 919 | |
| 920 void MetricsService::InitializeMetricsState(ReportingState reporting_state) { | |
| 921 #if defined(OS_POSIX) | |
| 922 network_stats_server_ = chrome_common_net::kEchoTestServerLocation; | |
| 923 http_pipelining_test_server_ = chrome_common_net::kPipelineTestServerBaseUrl; | |
| 924 #else | |
| 925 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); | |
| 926 network_stats_server_ = dist->GetNetworkStatsServer(); | |
| 927 http_pipelining_test_server_ = dist->GetHttpPipeliningTestServer(); | |
| 928 #endif | |
| 929 | |
| 930 PrefService* pref = g_browser_process->local_state(); | |
| 931 DCHECK(pref); | |
| 932 | |
| 933 pref->SetString(prefs::kStabilityStatsVersion, | |
| 934 MetricsLog::GetVersionString()); | |
| 935 pref->SetInt64(prefs::kStabilityStatsBuildTime, MetricsLog::GetBuildTime()); | |
| 936 | |
| 937 session_id_ = pref->GetInteger(prefs::kMetricsSessionID); | |
| 938 | |
| 939 #if defined(OS_ANDROID) | |
| 940 LogAndroidStabilityToPrefs(pref); | |
| 941 #endif // defined(OS_ANDROID) | |
| 942 | |
| 943 if (!pref->GetBoolean(prefs::kStabilityExitedCleanly)) { | |
| 944 IncrementPrefValue(prefs::kStabilityCrashCount); | |
| 945 // Reset flag, and wait until we call LogNeedForCleanShutdown() before | |
| 946 // monitoring. | |
| 947 pref->SetBoolean(prefs::kStabilityExitedCleanly, true); | |
| 948 | |
| 949 // TODO(rtenneti): On windows, consider saving/getting execution_phase from | |
| 950 // the registry. | |
| 951 int execution_phase = pref->GetInteger(prefs::kStabilityExecutionPhase); | |
| 952 UMA_HISTOGRAM_SPARSE_SLOWLY("Chrome.Browser.CrashedExecutionPhase", | |
| 953 execution_phase); | |
| 954 | |
| 955 // If the previous session didn't exit cleanly, then prepare an initial | |
| 956 // stability log if UMA is enabled. | |
| 957 if (reporting_state == REPORTING_ENABLED) | |
| 958 PrepareInitialStabilityLog(); | |
| 959 } | |
| 960 | |
| 961 // Update session ID. | |
| 962 ++session_id_; | |
| 963 pref->SetInteger(prefs::kMetricsSessionID, session_id_); | |
| 964 | |
| 965 // Stability bookkeeping | |
| 966 IncrementPrefValue(prefs::kStabilityLaunchCount); | |
| 967 | |
| 968 DCHECK_EQ(UNINITIALIZED_PHASE, execution_phase_); | |
| 969 SetExecutionPhase(START_METRICS_RECORDING); | |
| 970 | |
| 971 #if defined(OS_WIN) | |
| 972 CountBrowserCrashDumpAttempts(); | |
| 973 #endif // defined(OS_WIN) | |
| 974 | |
| 975 if (!pref->GetBoolean(prefs::kStabilitySessionEndCompleted)) { | |
| 976 IncrementPrefValue(prefs::kStabilityIncompleteSessionEndCount); | |
| 977 // This is marked false when we get a WM_ENDSESSION. | |
| 978 pref->SetBoolean(prefs::kStabilitySessionEndCompleted, true); | |
| 979 } | |
| 980 | |
| 981 // Call GetUptimes() for the first time, thus allowing all later calls | |
| 982 // to record incremental uptimes accurately. | |
| 983 base::TimeDelta ignored_uptime_parameter; | |
| 984 base::TimeDelta startup_uptime; | |
| 985 GetUptimes(pref, &startup_uptime, &ignored_uptime_parameter); | |
| 986 DCHECK_EQ(0, startup_uptime.InMicroseconds()); | |
| 987 // For backwards compatibility, leave this intact in case Omaha is checking | |
| 988 // them. prefs::kStabilityLastTimestampSec may also be useless now. | |
| 989 // TODO(jar): Delete these if they have no uses. | |
| 990 pref->SetInt64(prefs::kStabilityLaunchTimeSec, Time::Now().ToTimeT()); | |
| 991 | |
| 992 // Bookkeeping for the uninstall metrics. | |
| 993 IncrementLongPrefsValue(prefs::kUninstallLaunchCount); | |
| 994 | |
| 995 // Get stats on use of command line. | |
| 996 const CommandLine* command_line(CommandLine::ForCurrentProcess()); | |
| 997 size_t common_commands = 0; | |
| 998 if (command_line->HasSwitch(switches::kUserDataDir)) { | |
| 999 ++common_commands; | |
| 1000 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineDatDirCount", 1); | |
| 1001 } | |
| 1002 | |
| 1003 if (command_line->HasSwitch(switches::kApp)) { | |
| 1004 ++common_commands; | |
| 1005 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineAppModeCount", 1); | |
| 1006 } | |
| 1007 | |
| 1008 size_t switch_count = command_line->GetSwitches().size(); | |
| 1009 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineFlagCount", switch_count); | |
| 1010 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineUncommonFlagCount", | |
| 1011 switch_count - common_commands); | |
| 1012 | |
| 1013 // Kick off the process of saving the state (so the uptime numbers keep | |
| 1014 // getting updated) every n minutes. | |
| 1015 ScheduleNextStateSave(); | |
| 1016 } | |
| 1017 | |
| 1018 // static | |
| 1019 void MetricsService::InitTaskGetHardwareClass( | |
| 1020 base::WeakPtr<MetricsService> self, | |
| 1021 base::MessageLoopProxy* target_loop) { | |
| 1022 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 1023 | |
| 1024 std::string hardware_class; | |
| 1025 #if defined(OS_CHROMEOS) | |
| 1026 chromeos::system::StatisticsProvider::GetInstance()->GetMachineStatistic( | |
| 1027 "hardware_class", &hardware_class); | |
| 1028 #endif // OS_CHROMEOS | |
| 1029 | |
| 1030 target_loop->PostTask(FROM_HERE, | |
| 1031 base::Bind(&MetricsService::OnInitTaskGotHardwareClass, | |
| 1032 self, hardware_class)); | |
| 1033 } | |
| 1034 | |
| 1035 void MetricsService::OnInitTaskGotHardwareClass( | |
| 1036 const std::string& hardware_class) { | |
| 1037 DCHECK_EQ(INIT_TASK_SCHEDULED, state_); | |
| 1038 hardware_class_ = hardware_class; | |
| 1039 | |
| 1040 #if defined(ENABLE_PLUGINS) | |
| 1041 // Start the next part of the init task: loading plugin information. | |
| 1042 PluginService::GetInstance()->GetPlugins( | |
| 1043 base::Bind(&MetricsService::OnInitTaskGotPluginInfo, | |
| 1044 self_ptr_factory_.GetWeakPtr())); | |
| 1045 #else | |
| 1046 std::vector<content::WebPluginInfo> plugin_list_empty; | |
| 1047 OnInitTaskGotPluginInfo(plugin_list_empty); | |
| 1048 #endif // defined(ENABLE_PLUGINS) | |
| 1049 } | |
| 1050 | |
| 1051 void MetricsService::OnInitTaskGotPluginInfo( | |
| 1052 const std::vector<content::WebPluginInfo>& plugins) { | |
| 1053 DCHECK_EQ(INIT_TASK_SCHEDULED, state_); | |
| 1054 plugins_ = plugins; | |
| 1055 | |
| 1056 // Schedules a task on a blocking pool thread to gather Google Update | |
| 1057 // statistics (requires Registry reads). | |
| 1058 BrowserThread::PostBlockingPoolTask( | |
| 1059 FROM_HERE, | |
| 1060 base::Bind(&MetricsService::InitTaskGetGoogleUpdateData, | |
| 1061 self_ptr_factory_.GetWeakPtr(), | |
| 1062 base::MessageLoop::current()->message_loop_proxy())); | |
| 1063 } | |
| 1064 | |
| 1065 // static | |
| 1066 void MetricsService::InitTaskGetGoogleUpdateData( | |
| 1067 base::WeakPtr<MetricsService> self, | |
| 1068 base::MessageLoopProxy* target_loop) { | |
| 1069 GoogleUpdateMetrics google_update_metrics; | |
| 1070 | |
| 1071 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD) | |
| 1072 const bool system_install = GoogleUpdateSettings::IsSystemInstall(); | |
| 1073 | |
| 1074 google_update_metrics.is_system_install = system_install; | |
| 1075 google_update_metrics.last_started_au = | |
| 1076 GoogleUpdateSettings::GetGoogleUpdateLastStartedAU(system_install); | |
| 1077 google_update_metrics.last_checked = | |
| 1078 GoogleUpdateSettings::GetGoogleUpdateLastChecked(system_install); | |
| 1079 GoogleUpdateSettings::GetUpdateDetailForGoogleUpdate( | |
| 1080 system_install, | |
| 1081 &google_update_metrics.google_update_data); | |
| 1082 GoogleUpdateSettings::GetUpdateDetail( | |
| 1083 system_install, | |
| 1084 &google_update_metrics.product_data); | |
| 1085 #endif // defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD) | |
| 1086 | |
| 1087 target_loop->PostTask(FROM_HERE, | |
| 1088 base::Bind(&MetricsService::OnInitTaskGotGoogleUpdateData, | |
| 1089 self, google_update_metrics)); | |
| 1090 } | |
| 1091 | |
| 1092 void MetricsService::OnInitTaskGotGoogleUpdateData( | |
| 1093 const GoogleUpdateMetrics& google_update_metrics) { | |
| 1094 DCHECK_EQ(INIT_TASK_SCHEDULED, state_); | |
| 1095 | |
| 1096 google_update_metrics_ = google_update_metrics; | |
| 1097 | |
| 1098 // Start the next part of the init task: fetching performance data. This will | |
| 1099 // call into |FinishedReceivingProfilerData()| when the task completes. | |
| 1100 chrome_browser_metrics::TrackingSynchronizer::FetchProfilerDataAsynchronously( | |
| 1101 self_ptr_factory_.GetWeakPtr()); | |
| 1102 } | |
| 1103 | |
| 1104 void MetricsService::OnUserAction(const std::string& action) { | |
| 1105 if (!ShouldLogEvents()) | |
| 1106 return; | |
| 1107 | |
| 1108 log_manager_.current_log()->RecordUserAction(action); | |
| 1109 HandleIdleSinceLastTransmission(false); | |
| 1110 } | |
| 1111 | |
| 1112 void MetricsService::ReceivedProfilerData( | |
| 1113 const tracked_objects::ProcessDataSnapshot& process_data, | |
| 1114 int process_type) { | |
| 1115 DCHECK_EQ(INIT_TASK_SCHEDULED, state_); | |
| 1116 | |
| 1117 // Upon the first callback, create the initial log so that we can immediately | |
| 1118 // save the profiler data. | |
| 1119 if (!initial_metrics_log_.get()) { | |
| 1120 initial_metrics_log_.reset( | |
| 1121 new MetricsLog(client_id_, session_id_, MetricsLog::ONGOING_LOG)); | |
| 1122 } | |
| 1123 | |
| 1124 initial_metrics_log_->RecordProfilerData(process_data, process_type); | |
| 1125 } | |
| 1126 | |
| 1127 void MetricsService::FinishedReceivingProfilerData() { | |
| 1128 DCHECK_EQ(INIT_TASK_SCHEDULED, state_); | |
| 1129 state_ = INIT_TASK_DONE; | |
| 1130 scheduler_->InitTaskComplete(); | |
| 1131 } | |
| 1132 | |
| 1133 void MetricsService::GetUptimes(PrefService* pref, | |
| 1134 base::TimeDelta* incremental_uptime, | |
| 1135 base::TimeDelta* uptime) { | |
| 1136 base::TimeTicks now = base::TimeTicks::Now(); | |
| 1137 // If this is the first call, init |first_updated_time_| and | |
| 1138 // |last_updated_time_|. | |
| 1139 if (last_updated_time_.is_null()) { | |
| 1140 first_updated_time_ = now; | |
| 1141 last_updated_time_ = now; | |
| 1142 } | |
| 1143 *incremental_uptime = now - last_updated_time_; | |
| 1144 *uptime = now - first_updated_time_; | |
| 1145 last_updated_time_ = now; | |
| 1146 | |
| 1147 const int64 incremental_time_secs = incremental_uptime->InSeconds(); | |
| 1148 if (incremental_time_secs > 0) { | |
| 1149 int64 metrics_uptime = pref->GetInt64(prefs::kUninstallMetricsUptimeSec); | |
| 1150 metrics_uptime += incremental_time_secs; | |
| 1151 pref->SetInt64(prefs::kUninstallMetricsUptimeSec, metrics_uptime); | |
| 1152 } | |
| 1153 } | |
| 1154 | |
| 1155 void MetricsService::ResetMetricsIDsIfNecessary() { | |
| 1156 if (metrics_ids_reset_check_performed_) | |
| 1157 return; | |
| 1158 | |
| 1159 metrics_ids_reset_check_performed_ = true; | |
| 1160 | |
| 1161 PrefService* local_state = g_browser_process->local_state(); | |
| 1162 if (!local_state->GetBoolean(prefs::kMetricsResetIds)) | |
| 1163 return; | |
| 1164 | |
| 1165 UMA_HISTOGRAM_BOOLEAN("UMA.MetricsIDsReset", true); | |
| 1166 | |
| 1167 DCHECK(client_id_.empty()); | |
| 1168 DCHECK_EQ(kLowEntropySourceNotSet, low_entropy_source_); | |
| 1169 | |
| 1170 local_state->ClearPref(prefs::kMetricsClientID); | |
| 1171 local_state->ClearPref(prefs::kMetricsLowEntropySource); | |
| 1172 local_state->ClearPref(prefs::kMetricsResetIds); | |
| 1173 } | |
| 1174 | |
| 1175 int MetricsService::GetLowEntropySource() { | |
| 1176 // Note that the default value for the low entropy source and the default pref | 172 // Note that the default value for the low entropy source and the default pref |
| 1177 // value are both kLowEntropySourceNotSet, which is used to identify if the | 173 // value are both kLowEntropySourceNotSet, which is used to identify if the |
| 1178 // value has been set or not. | 174 // value has been set or not. |
| 1179 if (low_entropy_source_ != kLowEntropySourceNotSet) | 175 if (low_entropy_source_ != kLowEntropySourceNotSet) |
| 1180 return low_entropy_source_; | 176 return low_entropy_source_; |
| 1181 | 177 |
| 1182 ResetMetricsIDsIfNecessary(); | |
| 1183 | |
| 1184 PrefService* local_state = g_browser_process->local_state(); | |
| 1185 const CommandLine* command_line(CommandLine::ForCurrentProcess()); | 178 const CommandLine* command_line(CommandLine::ForCurrentProcess()); |
| 1186 // Only try to load the value from prefs if the user did not request a reset. | 179 // Only try to load the value from prefs if the user did not request a reset. |
| 1187 // Otherwise, skip to generating a new value. | 180 // Otherwise, skip to generating a new value. |
| 1188 if (!command_line->HasSwitch(switches::kResetVariationState)) { | 181 if (!command_line->HasSwitch(switches::kResetVariationState)) { |
| 1189 int value = local_state->GetInteger(prefs::kMetricsLowEntropySource); | 182 int value = local_state_->GetInteger(prefs::kMetricsLowEntropySource); |
| 1190 // If the value is outside the [0, kMaxLowEntropySize) range, re-generate | 183 // If the value is outside the [0, kMaxLowEntropySize) range, re-generate |
| 1191 // it below. | 184 // it below. |
| 1192 if (value >= 0 && value < kMaxLowEntropySize) { | 185 if (value >= 0 && value < kMaxLowEntropySize) { |
| 1193 low_entropy_source_ = value; | 186 low_entropy_source_ = value; |
| 1194 UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", false); | 187 UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", false); |
| 1195 return low_entropy_source_; | 188 return low_entropy_source_; |
| 1196 } | 189 } |
| 1197 } | 190 } |
| 1198 | 191 |
| 1199 UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", true); | 192 UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", true); |
| 1200 low_entropy_source_ = GenerateLowEntropySource(); | 193 low_entropy_source_ = GenerateLowEntropySource(); |
| 1201 local_state->SetInteger(prefs::kMetricsLowEntropySource, low_entropy_source_); | 194 local_state_->SetInteger(prefs::kMetricsLowEntropySource, |
| 1202 local_state->ClearPref(prefs::kMetricsOldLowEntropySource); | 195 low_entropy_source_); |
| 1203 metrics::CachingPermutedEntropyProvider::ClearCache(local_state); | 196 local_state_->ClearPref(prefs::kMetricsOldLowEntropySource); |
| 197 metrics::CachingPermutedEntropyProvider::ClearCache(local_state_); | |
| 1204 | 198 |
| 1205 return low_entropy_source_; | 199 return low_entropy_source_; |
| 1206 } | 200 } |
| 1207 | 201 |
| 1208 // static | 202 void MetricsStateManager::ResetMetricsIDsIfNecessary() { |
| 1209 std::string MetricsService::GenerateClientID() { | 203 if (!local_state_->GetBoolean(prefs::kMetricsResetIds)) |
| 1210 return base::GenerateGUID(); | 204 return; |
| 205 | |
| 206 UMA_HISTOGRAM_BOOLEAN("UMA.MetricsIDsReset", true); | |
| 207 | |
| 208 DCHECK(client_id_.empty()); | |
| 209 DCHECK_EQ(kLowEntropySourceNotSet, low_entropy_source_); | |
| 210 | |
| 211 local_state_->ClearPref(prefs::kMetricsClientID); | |
| 212 local_state_->ClearPref(prefs::kMetricsLowEntropySource); | |
| 213 local_state_->ClearPref(prefs::kMetricsResetIds); | |
| 1211 } | 214 } |
| 1212 | 215 |
| 1213 //------------------------------------------------------------------------------ | 216 } // namespace metrics |
| 1214 // State save methods | |
| 1215 | |
| 1216 void MetricsService::ScheduleNextStateSave() { | |
| 1217 state_saver_factory_.InvalidateWeakPtrs(); | |
| 1218 | |
| 1219 base::MessageLoop::current()->PostDelayedTask(FROM_HERE, | |
| 1220 base::Bind(&MetricsService::SaveLocalState, | |
| 1221 state_saver_factory_.GetWeakPtr()), | |
| 1222 base::TimeDelta::FromMinutes(kSaveStateIntervalMinutes)); | |
| 1223 } | |
| 1224 | |
| 1225 void MetricsService::SaveLocalState() { | |
| 1226 PrefService* pref = g_browser_process->local_state(); | |
| 1227 if (!pref) { | |
| 1228 NOTREACHED(); | |
| 1229 return; | |
| 1230 } | |
| 1231 | |
| 1232 RecordCurrentState(pref); | |
| 1233 | |
| 1234 // TODO(jar):110021 Does this run down the batteries???? | |
| 1235 ScheduleNextStateSave(); | |
| 1236 } | |
| 1237 | |
| 1238 | |
| 1239 //------------------------------------------------------------------------------ | |
| 1240 // Recording control methods | |
| 1241 | |
| 1242 void MetricsService::OpenNewLog() { | |
| 1243 DCHECK(!log_manager_.current_log()); | |
| 1244 | |
| 1245 log_manager_.BeginLoggingWithLog( | |
| 1246 new MetricsLog(client_id_, session_id_, MetricsLog::ONGOING_LOG)); | |
| 1247 if (state_ == INITIALIZED) { | |
| 1248 // We only need to schedule that run once. | |
| 1249 state_ = INIT_TASK_SCHEDULED; | |
| 1250 | |
| 1251 // Schedules a task on the file thread for execution of slower | |
| 1252 // initialization steps (such as plugin list generation) necessary | |
| 1253 // for sending the initial log. This avoids blocking the main UI | |
| 1254 // thread. | |
| 1255 BrowserThread::PostDelayedTask( | |
| 1256 BrowserThread::FILE, | |
| 1257 FROM_HERE, | |
| 1258 base::Bind(&MetricsService::InitTaskGetHardwareClass, | |
| 1259 self_ptr_factory_.GetWeakPtr(), | |
| 1260 base::MessageLoop::current()->message_loop_proxy()), | |
| 1261 base::TimeDelta::FromSeconds(kInitializationDelaySeconds)); | |
| 1262 } | |
| 1263 } | |
| 1264 | |
| 1265 void MetricsService::CloseCurrentLog() { | |
| 1266 if (!log_manager_.current_log()) | |
| 1267 return; | |
| 1268 | |
| 1269 // TODO(jar): Integrate bounds on log recording more consistently, so that we | |
| 1270 // can stop recording logs that are too big much sooner. | |
| 1271 if (log_manager_.current_log()->num_events() > kEventLimit) { | |
| 1272 UMA_HISTOGRAM_COUNTS("UMA.Discarded Log Events", | |
| 1273 log_manager_.current_log()->num_events()); | |
| 1274 log_manager_.DiscardCurrentLog(); | |
| 1275 OpenNewLog(); // Start trivial log to hold our histograms. | |
| 1276 } | |
| 1277 | |
| 1278 // Adds to ongoing logs. | |
| 1279 log_manager_.current_log()->set_hardware_class(hardware_class_); | |
| 1280 | |
| 1281 // Put incremental data (histogram deltas, and realtime stats deltas) at the | |
| 1282 // end of all log transmissions (initial log handles this separately). | |
| 1283 // RecordIncrementalStabilityElements only exists on the derived | |
| 1284 // MetricsLog class. | |
| 1285 MetricsLog* current_log = | |
| 1286 static_cast<MetricsLog*>(log_manager_.current_log()); | |
| 1287 DCHECK(current_log); | |
| 1288 std::vector<chrome_variations::ActiveGroupId> synthetic_trials; | |
| 1289 GetCurrentSyntheticFieldTrials(&synthetic_trials); | |
| 1290 current_log->RecordEnvironment(plugins_, google_update_metrics_, | |
| 1291 synthetic_trials); | |
| 1292 PrefService* pref = g_browser_process->local_state(); | |
| 1293 base::TimeDelta incremental_uptime; | |
| 1294 base::TimeDelta uptime; | |
| 1295 GetUptimes(pref, &incremental_uptime, &uptime); | |
| 1296 current_log->RecordStabilityMetrics(incremental_uptime, uptime); | |
| 1297 | |
| 1298 RecordCurrentHistograms(); | |
| 1299 | |
| 1300 log_manager_.FinishCurrentLog(); | |
| 1301 } | |
| 1302 | |
| 1303 void MetricsService::PushPendingLogsToPersistentStorage() { | |
| 1304 if (state_ < SENDING_INITIAL_STABILITY_LOG) | |
| 1305 return; // We didn't and still don't have time to get plugin list etc. | |
| 1306 | |
| 1307 if (log_manager_.has_staged_log()) { | |
| 1308 // We may race here, and send second copy of the log later. | |
| 1309 MetricsLogManager::StoreType store_type; | |
| 1310 if (current_fetch_.get()) | |
| 1311 store_type = MetricsLogManager::PROVISIONAL_STORE; | |
| 1312 else | |
| 1313 store_type = MetricsLogManager::NORMAL_STORE; | |
| 1314 log_manager_.StoreStagedLogAsUnsent(store_type); | |
| 1315 } | |
| 1316 DCHECK(!log_manager_.has_staged_log()); | |
| 1317 CloseCurrentLog(); | |
| 1318 log_manager_.PersistUnsentLogs(); | |
| 1319 | |
| 1320 // If there was a staged and/or current log, then there is now at least one | |
| 1321 // log waiting to be uploaded. | |
| 1322 if (log_manager_.has_unsent_logs()) | |
| 1323 state_ = SENDING_OLD_LOGS; | |
| 1324 } | |
| 1325 | |
| 1326 //------------------------------------------------------------------------------ | |
| 1327 // Transmission of logs methods | |
| 1328 | |
| 1329 void MetricsService::StartSchedulerIfNecessary() { | |
| 1330 // Never schedule cutting or uploading of logs in test mode. | |
| 1331 if (test_mode_active_) | |
| 1332 return; | |
| 1333 | |
| 1334 // Even if reporting is disabled, the scheduler is needed to trigger the | |
| 1335 // creation of the initial log, which must be done in order for any logs to be | |
| 1336 // persisted on shutdown or backgrounding. | |
| 1337 if (recording_active() && | |
| 1338 (reporting_active() || state_ < SENDING_INITIAL_STABILITY_LOG)) { | |
| 1339 scheduler_->Start(); | |
| 1340 } | |
| 1341 } | |
| 1342 | |
| 1343 void MetricsService::StartScheduledUpload() { | |
| 1344 // If we're getting no notifications, then the log won't have much in it, and | |
| 1345 // it's possible the computer is about to go to sleep, so don't upload and | |
| 1346 // stop the scheduler. | |
| 1347 // If recording has been turned off, the scheduler doesn't need to run. | |
| 1348 // If reporting is off, proceed if the initial log hasn't been created, since | |
| 1349 // that has to happen in order for logs to be cut and stored when persisting. | |
| 1350 // TODO(stuartmorgan): Call Stop() on the schedule when reporting and/or | |
| 1351 // recording are turned off instead of letting it fire and then aborting. | |
| 1352 if (idle_since_last_transmission_ || | |
| 1353 !recording_active() || | |
| 1354 (!reporting_active() && state_ >= SENDING_INITIAL_STABILITY_LOG)) { | |
| 1355 scheduler_->Stop(); | |
| 1356 scheduler_->UploadCancelled(); | |
| 1357 return; | |
| 1358 } | |
| 1359 | |
| 1360 // If the callback was to upload an old log, but there no longer is one, | |
| 1361 // just report success back to the scheduler to begin the ongoing log | |
| 1362 // callbacks. | |
| 1363 // TODO(stuartmorgan): Consider removing the distinction between | |
| 1364 // SENDING_OLD_LOGS and SENDING_CURRENT_LOGS to simplify the state machine | |
| 1365 // now that the log upload flow is the same for both modes. | |
| 1366 if (state_ == SENDING_OLD_LOGS && !log_manager_.has_unsent_logs()) { | |
| 1367 state_ = SENDING_CURRENT_LOGS; | |
| 1368 scheduler_->UploadFinished(true /* healthy */, false /* no unsent logs */); | |
| 1369 return; | |
| 1370 } | |
| 1371 // If there are unsent logs, send the next one. If not, start the asynchronous | |
| 1372 // process of finalizing the current log for upload. | |
| 1373 if (state_ == SENDING_OLD_LOGS) { | |
| 1374 DCHECK(log_manager_.has_unsent_logs()); | |
| 1375 log_manager_.StageNextLogForUpload(); | |
| 1376 SendStagedLog(); | |
| 1377 } else { | |
| 1378 StartFinalLogInfoCollection(); | |
| 1379 } | |
| 1380 } | |
| 1381 | |
| 1382 void MetricsService::StartFinalLogInfoCollection() { | |
| 1383 // Begin the multi-step process of collecting memory usage histograms: | |
| 1384 // First spawn a task to collect the memory details; when that task is | |
| 1385 // finished, it will call OnMemoryDetailCollectionDone. That will in turn | |
| 1386 // call HistogramSynchronization to collect histograms from all renderers and | |
| 1387 // then call OnHistogramSynchronizationDone to continue processing. | |
| 1388 DCHECK(!waiting_for_asynchronous_reporting_step_); | |
| 1389 waiting_for_asynchronous_reporting_step_ = true; | |
| 1390 | |
| 1391 base::Closure callback = | |
| 1392 base::Bind(&MetricsService::OnMemoryDetailCollectionDone, | |
| 1393 self_ptr_factory_.GetWeakPtr()); | |
| 1394 | |
| 1395 scoped_refptr<MetricsMemoryDetails> details( | |
| 1396 new MetricsMemoryDetails(callback)); | |
| 1397 details->StartFetch(MemoryDetails::UPDATE_USER_METRICS); | |
| 1398 | |
| 1399 // Collect WebCore cache information to put into a histogram. | |
| 1400 for (content::RenderProcessHost::iterator i( | |
| 1401 content::RenderProcessHost::AllHostsIterator()); | |
| 1402 !i.IsAtEnd(); i.Advance()) | |
| 1403 i.GetCurrentValue()->Send(new ChromeViewMsg_GetCacheResourceStats()); | |
| 1404 } | |
| 1405 | |
| 1406 void MetricsService::OnMemoryDetailCollectionDone() { | |
| 1407 DCHECK(IsSingleThreaded()); | |
| 1408 // This function should only be called as the callback from an ansynchronous | |
| 1409 // step. | |
| 1410 DCHECK(waiting_for_asynchronous_reporting_step_); | |
| 1411 | |
| 1412 // Create a callback_task for OnHistogramSynchronizationDone. | |
| 1413 base::Closure callback = base::Bind( | |
| 1414 &MetricsService::OnHistogramSynchronizationDone, | |
| 1415 self_ptr_factory_.GetWeakPtr()); | |
| 1416 | |
| 1417 base::TimeDelta timeout = | |
| 1418 base::TimeDelta::FromMilliseconds(kMaxHistogramGatheringWaitDuration); | |
| 1419 | |
| 1420 DCHECK_EQ(num_async_histogram_fetches_in_progress_, 0); | |
| 1421 | |
| 1422 #if defined(OS_ANDROID) | |
| 1423 // Android has no service process. | |
| 1424 num_async_histogram_fetches_in_progress_ = 1; | |
| 1425 #else // OS_ANDROID | |
| 1426 num_async_histogram_fetches_in_progress_ = 2; | |
| 1427 // Run requests to service and content in parallel. | |
| 1428 if (!ServiceProcessControl::GetInstance()->GetHistograms(callback, timeout)) { | |
| 1429 // Assume |num_async_histogram_fetches_in_progress_| is not changed by | |
| 1430 // |GetHistograms()|. | |
| 1431 DCHECK_EQ(num_async_histogram_fetches_in_progress_, 2); | |
| 1432 // Assign |num_async_histogram_fetches_in_progress_| above and decrement it | |
| 1433 // here to make code work even if |GetHistograms()| fired |callback|. | |
| 1434 --num_async_histogram_fetches_in_progress_; | |
| 1435 } | |
| 1436 #endif // OS_ANDROID | |
| 1437 | |
| 1438 // Set up the callback to task to call after we receive histograms from all | |
| 1439 // child processes. Wait time specifies how long to wait before absolutely | |
| 1440 // calling us back on the task. | |
| 1441 content::FetchHistogramsAsynchronously(base::MessageLoop::current(), callback, | |
| 1442 timeout); | |
| 1443 } | |
| 1444 | |
| 1445 void MetricsService::OnHistogramSynchronizationDone() { | |
| 1446 DCHECK(IsSingleThreaded()); | |
| 1447 // This function should only be called as the callback from an ansynchronous | |
| 1448 // step. | |
| 1449 DCHECK(waiting_for_asynchronous_reporting_step_); | |
| 1450 DCHECK_GT(num_async_histogram_fetches_in_progress_, 0); | |
| 1451 | |
| 1452 // Check if all expected requests finished. | |
| 1453 if (--num_async_histogram_fetches_in_progress_ > 0) | |
| 1454 return; | |
| 1455 | |
| 1456 waiting_for_asynchronous_reporting_step_ = false; | |
| 1457 OnFinalLogInfoCollectionDone(); | |
| 1458 } | |
| 1459 | |
| 1460 void MetricsService::OnFinalLogInfoCollectionDone() { | |
| 1461 // If somehow there is a fetch in progress, we return and hope things work | |
| 1462 // out. The scheduler isn't informed since if this happens, the scheduler | |
| 1463 // will get a response from the upload. | |
| 1464 DCHECK(!current_fetch_.get()); | |
| 1465 if (current_fetch_.get()) | |
| 1466 return; | |
| 1467 | |
| 1468 // Abort if metrics were turned off during the final info gathering. | |
| 1469 if (!recording_active()) { | |
| 1470 scheduler_->Stop(); | |
| 1471 scheduler_->UploadCancelled(); | |
| 1472 return; | |
| 1473 } | |
| 1474 | |
| 1475 StageNewLog(); | |
| 1476 | |
| 1477 // If logs shouldn't be uploaded, stop here. It's important that this check | |
| 1478 // be after StageNewLog(), otherwise the previous logs will never be loaded, | |
| 1479 // and thus the open log won't be persisted. | |
| 1480 // TODO(stuartmorgan): This is unnecessarily complicated; restructure loading | |
| 1481 // of previous logs to not require running part of the upload logic. | |
| 1482 // http://crbug.com/157337 | |
| 1483 if (!reporting_active()) { | |
| 1484 scheduler_->Stop(); | |
| 1485 scheduler_->UploadCancelled(); | |
| 1486 return; | |
| 1487 } | |
| 1488 | |
| 1489 SendStagedLog(); | |
| 1490 } | |
| 1491 | |
| 1492 void MetricsService::StageNewLog() { | |
| 1493 if (log_manager_.has_staged_log()) | |
| 1494 return; | |
| 1495 | |
| 1496 switch (state_) { | |
| 1497 case INITIALIZED: | |
| 1498 case INIT_TASK_SCHEDULED: // We should be further along by now. | |
| 1499 NOTREACHED(); | |
| 1500 return; | |
| 1501 | |
| 1502 case INIT_TASK_DONE: | |
| 1503 if (has_initial_stability_log_) { | |
| 1504 // There's an initial stability log, ready to send. | |
| 1505 log_manager_.StageNextLogForUpload(); | |
| 1506 has_initial_stability_log_ = false; | |
| 1507 // Note: No need to call LoadPersistedUnsentLogs() here because unsent | |
| 1508 // logs have already been loaded by PrepareInitialStabilityLog(). | |
| 1509 state_ = SENDING_INITIAL_STABILITY_LOG; | |
| 1510 } else { | |
| 1511 PrepareInitialMetricsLog(); | |
| 1512 // Load unsent logs (if any) from local state. | |
| 1513 log_manager_.LoadPersistedUnsentLogs(); | |
| 1514 state_ = SENDING_INITIAL_METRICS_LOG; | |
| 1515 } | |
| 1516 break; | |
| 1517 | |
| 1518 case SENDING_OLD_LOGS: | |
| 1519 NOTREACHED(); // Shouldn't be staging a new log during old log sending. | |
| 1520 return; | |
| 1521 | |
| 1522 case SENDING_CURRENT_LOGS: | |
| 1523 CloseCurrentLog(); | |
| 1524 OpenNewLog(); | |
| 1525 log_manager_.StageNextLogForUpload(); | |
| 1526 break; | |
| 1527 | |
| 1528 default: | |
| 1529 NOTREACHED(); | |
| 1530 return; | |
| 1531 } | |
| 1532 | |
| 1533 DCHECK(log_manager_.has_staged_log()); | |
| 1534 } | |
| 1535 | |
| 1536 void MetricsService::PrepareInitialStabilityLog() { | |
| 1537 DCHECK_EQ(INITIALIZED, state_); | |
| 1538 PrefService* pref = g_browser_process->local_state(); | |
| 1539 DCHECK_NE(0, pref->GetInteger(prefs::kStabilityCrashCount)); | |
| 1540 | |
| 1541 scoped_ptr<MetricsLog> initial_stability_log( | |
| 1542 new MetricsLog(client_id_, session_id_, | |
| 1543 MetricsLog::INITIAL_STABILITY_LOG)); | |
| 1544 if (!initial_stability_log->LoadSavedEnvironmentFromPrefs()) | |
| 1545 return; | |
| 1546 initial_stability_log->RecordStabilityMetrics(base::TimeDelta(), | |
| 1547 base::TimeDelta()); | |
| 1548 log_manager_.LoadPersistedUnsentLogs(); | |
| 1549 | |
| 1550 log_manager_.PauseCurrentLog(); | |
| 1551 log_manager_.BeginLoggingWithLog(initial_stability_log.release()); | |
| 1552 #if defined(OS_ANDROID) | |
| 1553 ConvertAndroidStabilityPrefsToHistograms(pref); | |
| 1554 RecordCurrentStabilityHistograms(); | |
| 1555 #endif // defined(OS_ANDROID) | |
| 1556 log_manager_.FinishCurrentLog(); | |
| 1557 log_manager_.ResumePausedLog(); | |
| 1558 | |
| 1559 // Store unsent logs, including the stability log that was just saved, so | |
| 1560 // that they're not lost in case of a crash before upload time. | |
| 1561 log_manager_.PersistUnsentLogs(); | |
| 1562 | |
| 1563 has_initial_stability_log_ = true; | |
| 1564 } | |
| 1565 | |
| 1566 void MetricsService::PrepareInitialMetricsLog() { | |
| 1567 DCHECK(state_ == INIT_TASK_DONE || state_ == SENDING_INITIAL_STABILITY_LOG); | |
| 1568 initial_metrics_log_->set_hardware_class(hardware_class_); | |
| 1569 | |
| 1570 std::vector<chrome_variations::ActiveGroupId> synthetic_trials; | |
| 1571 GetCurrentSyntheticFieldTrials(&synthetic_trials); | |
| 1572 initial_metrics_log_->RecordEnvironment(plugins_, google_update_metrics_, | |
| 1573 synthetic_trials); | |
| 1574 PrefService* pref = g_browser_process->local_state(); | |
| 1575 base::TimeDelta incremental_uptime; | |
| 1576 base::TimeDelta uptime; | |
| 1577 GetUptimes(pref, &incremental_uptime, &uptime); | |
| 1578 initial_metrics_log_->RecordStabilityMetrics(incremental_uptime, uptime); | |
| 1579 | |
| 1580 // Histograms only get written to the current log, so make the new log current | |
| 1581 // before writing them. | |
| 1582 log_manager_.PauseCurrentLog(); | |
| 1583 log_manager_.BeginLoggingWithLog(initial_metrics_log_.release()); | |
| 1584 #if defined(OS_ANDROID) | |
| 1585 ConvertAndroidStabilityPrefsToHistograms(pref); | |
| 1586 #endif // defined(OS_ANDROID) | |
| 1587 RecordCurrentHistograms(); | |
| 1588 log_manager_.FinishCurrentLog(); | |
| 1589 log_manager_.ResumePausedLog(); | |
| 1590 | |
| 1591 DCHECK(!log_manager_.has_staged_log()); | |
| 1592 log_manager_.StageNextLogForUpload(); | |
| 1593 } | |
| 1594 | |
| 1595 void MetricsService::SendStagedLog() { | |
| 1596 DCHECK(log_manager_.has_staged_log()); | |
| 1597 | |
| 1598 PrepareFetchWithStagedLog(); | |
| 1599 | |
| 1600 bool upload_created = (current_fetch_.get() != NULL); | |
| 1601 UMA_HISTOGRAM_BOOLEAN("UMA.UploadCreation", upload_created); | |
| 1602 if (!upload_created) { | |
| 1603 // Compression failed, and log discarded :-/. | |
| 1604 // Skip this upload and hope things work out next time. | |
| 1605 log_manager_.DiscardStagedLog(); | |
| 1606 scheduler_->UploadCancelled(); | |
| 1607 return; | |
| 1608 } | |
| 1609 | |
| 1610 DCHECK(!waiting_for_asynchronous_reporting_step_); | |
| 1611 waiting_for_asynchronous_reporting_step_ = true; | |
| 1612 | |
| 1613 current_fetch_->Start(); | |
| 1614 | |
| 1615 HandleIdleSinceLastTransmission(true); | |
| 1616 } | |
| 1617 | |
| 1618 void MetricsService::PrepareFetchWithStagedLog() { | |
| 1619 DCHECK(log_manager_.has_staged_log()); | |
| 1620 | |
| 1621 // Prepare the protobuf version. | |
| 1622 DCHECK(!current_fetch_.get()); | |
| 1623 if (log_manager_.has_staged_log()) { | |
| 1624 current_fetch_.reset(net::URLFetcher::Create( | |
| 1625 GURL(kServerUrl), net::URLFetcher::POST, this)); | |
| 1626 current_fetch_->SetRequestContext( | |
| 1627 g_browser_process->system_request_context()); | |
| 1628 | |
| 1629 std::string log_text = log_manager_.staged_log_text(); | |
| 1630 std::string compressed_log_text; | |
| 1631 bool compression_successful = chrome::GzipCompress(log_text, | |
| 1632 &compressed_log_text); | |
| 1633 DCHECK(compression_successful); | |
| 1634 if (compression_successful) { | |
| 1635 current_fetch_->SetUploadData(kMimeType, compressed_log_text); | |
| 1636 // Tell the server that we're uploading gzipped protobufs. | |
| 1637 current_fetch_->SetExtraRequestHeaders("content-encoding: gzip"); | |
| 1638 const std::string hash = | |
| 1639 base::HexEncode(log_manager_.staged_log_hash().data(), | |
| 1640 log_manager_.staged_log_hash().size()); | |
| 1641 DCHECK(!hash.empty()); | |
| 1642 current_fetch_->AddExtraRequestHeader("X-Chrome-UMA-Log-SHA1: " + hash); | |
| 1643 UMA_HISTOGRAM_PERCENTAGE( | |
| 1644 "UMA.ProtoCompressionRatio", | |
| 1645 100 * compressed_log_text.size() / log_text.size()); | |
| 1646 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
| 1647 "UMA.ProtoGzippedKBSaved", | |
| 1648 (log_text.size() - compressed_log_text.size()) / 1024, | |
| 1649 1, 2000, 50); | |
| 1650 } | |
| 1651 | |
| 1652 // We already drop cookies server-side, but we might as well strip them out | |
| 1653 // client-side as well. | |
| 1654 current_fetch_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES | | |
| 1655 net::LOAD_DO_NOT_SEND_COOKIES); | |
| 1656 } | |
| 1657 } | |
| 1658 | |
| 1659 void MetricsService::OnURLFetchComplete(const net::URLFetcher* source) { | |
| 1660 DCHECK(waiting_for_asynchronous_reporting_step_); | |
| 1661 | |
| 1662 // We're not allowed to re-use the existing |URLFetcher|s, so free them here. | |
| 1663 // Note however that |source| is aliased to the fetcher, so we should be | |
| 1664 // careful not to delete it too early. | |
| 1665 DCHECK_EQ(current_fetch_.get(), source); | |
| 1666 scoped_ptr<net::URLFetcher> s(current_fetch_.Pass()); | |
| 1667 | |
| 1668 int response_code = source->GetResponseCode(); | |
| 1669 | |
| 1670 // Log a histogram to track response success vs. failure rates. | |
| 1671 UMA_HISTOGRAM_ENUMERATION("UMA.UploadResponseStatus.Protobuf", | |
| 1672 ResponseCodeToStatus(response_code), | |
| 1673 NUM_RESPONSE_STATUSES); | |
| 1674 | |
| 1675 // If the upload was provisionally stored, drop it now that the upload is | |
| 1676 // known to have gone through. | |
| 1677 log_manager_.DiscardLastProvisionalStore(); | |
| 1678 | |
| 1679 bool upload_succeeded = response_code == 200; | |
| 1680 | |
| 1681 // Provide boolean for error recovery (allow us to ignore response_code). | |
| 1682 bool discard_log = false; | |
| 1683 const size_t log_size = log_manager_.staged_log_text().length(); | |
| 1684 if (!upload_succeeded && log_size > kUploadLogAvoidRetransmitSize) { | |
| 1685 UMA_HISTOGRAM_COUNTS("UMA.Large Rejected Log was Discarded", | |
| 1686 static_cast<int>(log_size)); | |
| 1687 discard_log = true; | |
| 1688 } else if (response_code == 400) { | |
| 1689 // Bad syntax. Retransmission won't work. | |
| 1690 discard_log = true; | |
| 1691 } | |
| 1692 | |
| 1693 if (upload_succeeded || discard_log) | |
| 1694 log_manager_.DiscardStagedLog(); | |
| 1695 | |
| 1696 waiting_for_asynchronous_reporting_step_ = false; | |
| 1697 | |
| 1698 if (!log_manager_.has_staged_log()) { | |
| 1699 switch (state_) { | |
| 1700 case SENDING_INITIAL_STABILITY_LOG: | |
| 1701 // Store the updated list to disk now that the removed log is uploaded. | |
| 1702 log_manager_.PersistUnsentLogs(); | |
| 1703 PrepareInitialMetricsLog(); | |
| 1704 SendStagedLog(); | |
| 1705 state_ = SENDING_INITIAL_METRICS_LOG; | |
| 1706 break; | |
| 1707 | |
| 1708 case SENDING_INITIAL_METRICS_LOG: | |
| 1709 // The initial metrics log never gets persisted to local state, so it's | |
| 1710 // not necessary to call log_manager_.PersistUnsentLogs() here. | |
| 1711 // TODO(asvitkine): It should be persisted like the initial stability | |
| 1712 // log and old unsent logs. http://crbug.com/328417 | |
| 1713 state_ = log_manager_.has_unsent_logs() ? SENDING_OLD_LOGS | |
| 1714 : SENDING_CURRENT_LOGS; | |
| 1715 break; | |
| 1716 | |
| 1717 case SENDING_OLD_LOGS: | |
| 1718 // Store the updated list to disk now that the removed log is uploaded. | |
| 1719 log_manager_.PersistUnsentLogs(); | |
| 1720 if (!log_manager_.has_unsent_logs()) | |
| 1721 state_ = SENDING_CURRENT_LOGS; | |
| 1722 break; | |
| 1723 | |
| 1724 case SENDING_CURRENT_LOGS: | |
| 1725 break; | |
| 1726 | |
| 1727 default: | |
| 1728 NOTREACHED(); | |
| 1729 break; | |
| 1730 } | |
| 1731 | |
| 1732 if (log_manager_.has_unsent_logs()) | |
| 1733 DCHECK_LT(state_, SENDING_CURRENT_LOGS); | |
| 1734 } | |
| 1735 | |
| 1736 // Error 400 indicates a problem with the log, not with the server, so | |
| 1737 // don't consider that a sign that the server is in trouble. | |
| 1738 bool server_is_healthy = upload_succeeded || response_code == 400; | |
| 1739 // Don't notify the scheduler that the upload is finished if we've only sent | |
| 1740 // the initial stability log, but not yet the initial metrics log (treat the | |
| 1741 // two as a single unit of work as far as the scheduler is concerned). | |
| 1742 if (state_ != SENDING_INITIAL_METRICS_LOG) { | |
| 1743 scheduler_->UploadFinished(server_is_healthy, | |
| 1744 log_manager_.has_unsent_logs()); | |
| 1745 } | |
| 1746 | |
| 1747 // Collect network stats if UMA upload succeeded. | |
| 1748 IOThread* io_thread = g_browser_process->io_thread(); | |
| 1749 if (server_is_healthy && io_thread) { | |
| 1750 chrome_browser_net::CollectNetworkStats(network_stats_server_, io_thread); | |
| 1751 chrome_browser_net::CollectPipeliningCapabilityStatsOnUIThread( | |
| 1752 http_pipelining_test_server_, io_thread); | |
| 1753 #if defined(OS_WIN) | |
| 1754 chrome::CollectTimeTicksStats(); | |
| 1755 #endif | |
| 1756 } | |
| 1757 } | |
| 1758 | |
| 1759 void MetricsService::IncrementPrefValue(const char* path) { | |
| 1760 PrefService* pref = g_browser_process->local_state(); | |
| 1761 DCHECK(pref); | |
| 1762 int value = pref->GetInteger(path); | |
| 1763 pref->SetInteger(path, value + 1); | |
| 1764 } | |
| 1765 | |
| 1766 void MetricsService::IncrementLongPrefsValue(const char* path) { | |
| 1767 PrefService* pref = g_browser_process->local_state(); | |
| 1768 DCHECK(pref); | |
| 1769 int64 value = pref->GetInt64(path); | |
| 1770 pref->SetInt64(path, value + 1); | |
| 1771 } | |
| 1772 | |
| 1773 void MetricsService::LogLoadStarted(content::WebContents* web_contents) { | |
| 1774 content::RecordAction(base::UserMetricsAction("PageLoad")); | |
| 1775 HISTOGRAM_ENUMERATION("Chrome.UmaPageloadCounter", 1, 2); | |
| 1776 IncrementPrefValue(prefs::kStabilityPageLoadCount); | |
| 1777 IncrementLongPrefsValue(prefs::kUninstallMetricsPageLoadCount); | |
| 1778 // We need to save the prefs, as page load count is a critical stat, and it | |
| 1779 // might be lost due to a crash :-(. | |
| 1780 } | |
| 1781 | |
| 1782 void MetricsService::LogRendererCrash(content::RenderProcessHost* host, | |
| 1783 base::TerminationStatus status, | |
| 1784 int exit_code) { | |
| 1785 bool was_extension_process = | |
| 1786 extensions::ProcessMap::Get(host->GetBrowserContext()) | |
| 1787 ->Contains(host->GetID()); | |
| 1788 if (status == base::TERMINATION_STATUS_PROCESS_CRASHED || | |
| 1789 status == base::TERMINATION_STATUS_ABNORMAL_TERMINATION) { | |
| 1790 if (was_extension_process) { | |
| 1791 IncrementPrefValue(prefs::kStabilityExtensionRendererCrashCount); | |
| 1792 | |
| 1793 UMA_HISTOGRAM_SPARSE_SLOWLY("CrashExitCodes.Extension", | |
| 1794 MapCrashExitCodeForHistogram(exit_code)); | |
| 1795 } else { | |
| 1796 IncrementPrefValue(prefs::kStabilityRendererCrashCount); | |
| 1797 | |
| 1798 UMA_HISTOGRAM_SPARSE_SLOWLY("CrashExitCodes.Renderer", | |
| 1799 MapCrashExitCodeForHistogram(exit_code)); | |
| 1800 } | |
| 1801 | |
| 1802 UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.ChildCrashes", | |
| 1803 was_extension_process ? 2 : 1); | |
| 1804 } else if (status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED) { | |
| 1805 UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.ChildKills", | |
| 1806 was_extension_process ? 2 : 1); | |
| 1807 } else if (status == base::TERMINATION_STATUS_STILL_RUNNING) { | |
| 1808 UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.DisconnectedAlive", | |
| 1809 was_extension_process ? 2 : 1); | |
| 1810 } | |
| 1811 } | |
| 1812 | |
| 1813 void MetricsService::LogRendererHang() { | |
| 1814 IncrementPrefValue(prefs::kStabilityRendererHangCount); | |
| 1815 } | |
| 1816 | |
| 1817 bool MetricsService::UmaMetricsProperlyShutdown() { | |
| 1818 CHECK(clean_shutdown_status_ == CLEANLY_SHUTDOWN || | |
| 1819 clean_shutdown_status_ == NEED_TO_SHUTDOWN); | |
| 1820 return clean_shutdown_status_ == CLEANLY_SHUTDOWN; | |
| 1821 } | |
| 1822 | |
| 1823 void MetricsService::RegisterSyntheticFieldTrial( | |
| 1824 const SyntheticTrialGroup& trial) { | |
| 1825 for (size_t i = 0; i < synthetic_trial_groups_.size(); ++i) { | |
| 1826 if (synthetic_trial_groups_[i].id.name == trial.id.name) { | |
| 1827 if (synthetic_trial_groups_[i].id.group != trial.id.group) { | |
| 1828 synthetic_trial_groups_[i].id.group = trial.id.group; | |
| 1829 synthetic_trial_groups_[i].start_time = base::TimeTicks::Now(); | |
| 1830 } | |
| 1831 return; | |
| 1832 } | |
| 1833 } | |
| 1834 | |
| 1835 SyntheticTrialGroup trial_group = trial; | |
| 1836 trial_group.start_time = base::TimeTicks::Now(); | |
| 1837 synthetic_trial_groups_.push_back(trial_group); | |
| 1838 } | |
| 1839 | |
| 1840 void MetricsService::CheckForClonedInstall() { | |
| 1841 DCHECK(!cloned_install_detector_); | |
| 1842 | |
| 1843 metrics::MachineIdProvider* provider = | |
| 1844 metrics::MachineIdProvider::CreateInstance(); | |
| 1845 if (!provider) | |
| 1846 return; | |
| 1847 | |
| 1848 cloned_install_detector_.reset( | |
| 1849 new metrics::ClonedInstallDetector(provider)); | |
| 1850 | |
| 1851 PrefService* local_state = g_browser_process->local_state(); | |
| 1852 cloned_install_detector_->CheckForClonedInstall(local_state); | |
| 1853 } | |
| 1854 | |
| 1855 void MetricsService::GetCurrentSyntheticFieldTrials( | |
| 1856 std::vector<chrome_variations::ActiveGroupId>* synthetic_trials) { | |
| 1857 DCHECK(synthetic_trials); | |
| 1858 synthetic_trials->clear(); | |
| 1859 const MetricsLog* current_log = | |
| 1860 static_cast<const MetricsLog*>(log_manager_.current_log()); | |
| 1861 for (size_t i = 0; i < synthetic_trial_groups_.size(); ++i) { | |
| 1862 if (synthetic_trial_groups_[i].start_time <= current_log->creation_time()) | |
| 1863 synthetic_trials->push_back(synthetic_trial_groups_[i].id); | |
| 1864 } | |
| 1865 } | |
| 1866 | |
| 1867 void MetricsService::LogCleanShutdown() { | |
| 1868 // Redundant hack to write pref ASAP. | |
| 1869 MarkAppCleanShutdownAndCommit(); | |
| 1870 | |
| 1871 // Redundant setting to assure that we always reset this value at shutdown | |
| 1872 // (and that we don't use some alternate path, and not call LogCleanShutdown). | |
| 1873 clean_shutdown_status_ = CLEANLY_SHUTDOWN; | |
| 1874 | |
| 1875 RecordBooleanPrefValue(prefs::kStabilityExitedCleanly, true); | |
| 1876 PrefService* pref = g_browser_process->local_state(); | |
| 1877 pref->SetInteger(prefs::kStabilityExecutionPhase, | |
| 1878 MetricsService::SHUTDOWN_COMPLETE); | |
| 1879 } | |
| 1880 | |
| 1881 #if defined(OS_CHROMEOS) | |
| 1882 void MetricsService::LogChromeOSCrash(const std::string &crash_type) { | |
| 1883 if (crash_type == "user") | |
| 1884 IncrementPrefValue(prefs::kStabilityOtherUserCrashCount); | |
| 1885 else if (crash_type == "kernel") | |
| 1886 IncrementPrefValue(prefs::kStabilityKernelCrashCount); | |
| 1887 else if (crash_type == "uncleanshutdown") | |
| 1888 IncrementPrefValue(prefs::kStabilitySystemUncleanShutdownCount); | |
| 1889 else | |
| 1890 NOTREACHED() << "Unexpected Chrome OS crash type " << crash_type; | |
| 1891 // Wake up metrics logs sending if necessary now that new | |
| 1892 // log data is available. | |
| 1893 HandleIdleSinceLastTransmission(false); | |
| 1894 } | |
| 1895 #endif // OS_CHROMEOS | |
| 1896 | |
| 1897 void MetricsService::LogPluginLoadingError(const base::FilePath& plugin_path) { | |
| 1898 content::WebPluginInfo plugin; | |
| 1899 bool success = | |
| 1900 content::PluginService::GetInstance()->GetPluginInfoByPath(plugin_path, | |
| 1901 &plugin); | |
| 1902 DCHECK(success); | |
| 1903 ChildProcessStats& stats = child_process_stats_buffer_[plugin.name]; | |
| 1904 // Initialize the type if this entry is new. | |
| 1905 if (stats.process_type == content::PROCESS_TYPE_UNKNOWN) { | |
| 1906 // The plug-in process might not actually of type PLUGIN (which means | |
| 1907 // NPAPI), but we only care that it is *a* plug-in process. | |
| 1908 stats.process_type = content::PROCESS_TYPE_PLUGIN; | |
| 1909 } else { | |
| 1910 DCHECK(IsPluginProcess(stats.process_type)); | |
| 1911 } | |
| 1912 stats.loading_errors++; | |
| 1913 } | |
| 1914 | |
| 1915 MetricsService::ChildProcessStats& MetricsService::GetChildProcessStats( | |
| 1916 const content::ChildProcessData& data) { | |
| 1917 const base::string16& child_name = data.name; | |
| 1918 if (!ContainsKey(child_process_stats_buffer_, child_name)) { | |
| 1919 child_process_stats_buffer_[child_name] = | |
| 1920 ChildProcessStats(data.process_type); | |
| 1921 } | |
| 1922 return child_process_stats_buffer_[child_name]; | |
| 1923 } | |
| 1924 | |
| 1925 void MetricsService::RecordPluginChanges(PrefService* pref) { | |
| 1926 ListPrefUpdate update(pref, prefs::kStabilityPluginStats); | |
| 1927 base::ListValue* plugins = update.Get(); | |
| 1928 DCHECK(plugins); | |
| 1929 | |
| 1930 for (base::ListValue::iterator value_iter = plugins->begin(); | |
| 1931 value_iter != plugins->end(); ++value_iter) { | |
| 1932 if (!(*value_iter)->IsType(base::Value::TYPE_DICTIONARY)) { | |
| 1933 NOTREACHED(); | |
| 1934 continue; | |
| 1935 } | |
| 1936 | |
| 1937 base::DictionaryValue* plugin_dict = | |
| 1938 static_cast<base::DictionaryValue*>(*value_iter); | |
| 1939 std::string plugin_name; | |
| 1940 plugin_dict->GetString(prefs::kStabilityPluginName, &plugin_name); | |
| 1941 if (plugin_name.empty()) { | |
| 1942 NOTREACHED(); | |
| 1943 continue; | |
| 1944 } | |
| 1945 | |
| 1946 // TODO(viettrungluu): remove conversions | |
| 1947 base::string16 name16 = base::UTF8ToUTF16(plugin_name); | |
| 1948 if (child_process_stats_buffer_.find(name16) == | |
| 1949 child_process_stats_buffer_.end()) { | |
| 1950 continue; | |
| 1951 } | |
| 1952 | |
| 1953 ChildProcessStats stats = child_process_stats_buffer_[name16]; | |
| 1954 if (stats.process_launches) { | |
| 1955 int launches = 0; | |
| 1956 plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches); | |
| 1957 launches += stats.process_launches; | |
| 1958 plugin_dict->SetInteger(prefs::kStabilityPluginLaunches, launches); | |
| 1959 } | |
| 1960 if (stats.process_crashes) { | |
| 1961 int crashes = 0; | |
| 1962 plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes); | |
| 1963 crashes += stats.process_crashes; | |
| 1964 plugin_dict->SetInteger(prefs::kStabilityPluginCrashes, crashes); | |
| 1965 } | |
| 1966 if (stats.instances) { | |
| 1967 int instances = 0; | |
| 1968 plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances); | |
| 1969 instances += stats.instances; | |
| 1970 plugin_dict->SetInteger(prefs::kStabilityPluginInstances, instances); | |
| 1971 } | |
| 1972 if (stats.loading_errors) { | |
| 1973 int loading_errors = 0; | |
| 1974 plugin_dict->GetInteger(prefs::kStabilityPluginLoadingErrors, | |
| 1975 &loading_errors); | |
| 1976 loading_errors += stats.loading_errors; | |
| 1977 plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors, | |
| 1978 loading_errors); | |
| 1979 } | |
| 1980 | |
| 1981 child_process_stats_buffer_.erase(name16); | |
| 1982 } | |
| 1983 | |
| 1984 // Now go through and add dictionaries for plugins that didn't already have | |
| 1985 // reports in Local State. | |
| 1986 for (std::map<base::string16, ChildProcessStats>::iterator cache_iter = | |
| 1987 child_process_stats_buffer_.begin(); | |
| 1988 cache_iter != child_process_stats_buffer_.end(); ++cache_iter) { | |
| 1989 ChildProcessStats stats = cache_iter->second; | |
| 1990 | |
| 1991 // Insert only plugins information into the plugins list. | |
| 1992 if (!IsPluginProcess(stats.process_type)) | |
| 1993 continue; | |
| 1994 | |
| 1995 // TODO(viettrungluu): remove conversion | |
| 1996 std::string plugin_name = base::UTF16ToUTF8(cache_iter->first); | |
| 1997 | |
| 1998 base::DictionaryValue* plugin_dict = new base::DictionaryValue; | |
| 1999 | |
| 2000 plugin_dict->SetString(prefs::kStabilityPluginName, plugin_name); | |
| 2001 plugin_dict->SetInteger(prefs::kStabilityPluginLaunches, | |
| 2002 stats.process_launches); | |
| 2003 plugin_dict->SetInteger(prefs::kStabilityPluginCrashes, | |
| 2004 stats.process_crashes); | |
| 2005 plugin_dict->SetInteger(prefs::kStabilityPluginInstances, | |
| 2006 stats.instances); | |
| 2007 plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors, | |
| 2008 stats.loading_errors); | |
| 2009 plugins->Append(plugin_dict); | |
| 2010 } | |
| 2011 child_process_stats_buffer_.clear(); | |
| 2012 } | |
| 2013 | |
| 2014 bool MetricsService::ShouldLogEvents() { | |
| 2015 // We simply don't log events to UMA if there is a single incognito | |
| 2016 // session visible. The problem is that we always notify using the orginal | |
| 2017 // profile in order to simplify notification processing. | |
| 2018 return !chrome::IsOffTheRecordSessionActive(); | |
| 2019 } | |
| 2020 | |
| 2021 void MetricsService::RecordBooleanPrefValue(const char* path, bool value) { | |
| 2022 DCHECK(IsSingleThreaded()); | |
| 2023 | |
| 2024 PrefService* pref = g_browser_process->local_state(); | |
| 2025 DCHECK(pref); | |
| 2026 | |
| 2027 pref->SetBoolean(path, value); | |
| 2028 RecordCurrentState(pref); | |
| 2029 } | |
| 2030 | |
| 2031 void MetricsService::RecordCurrentState(PrefService* pref) { | |
| 2032 pref->SetInt64(prefs::kStabilityLastTimestampSec, Time::Now().ToTimeT()); | |
| 2033 | |
| 2034 RecordPluginChanges(pref); | |
| 2035 } | |
| 2036 | |
| 2037 // static | |
| 2038 bool MetricsService::IsPluginProcess(int process_type) { | |
| 2039 return (process_type == content::PROCESS_TYPE_PLUGIN || | |
| 2040 process_type == content::PROCESS_TYPE_PPAPI_PLUGIN || | |
| 2041 process_type == content::PROCESS_TYPE_PPAPI_BROKER); | |
| 2042 } | |
| 2043 | |
| 2044 #if defined(OS_CHROMEOS) | |
| 2045 void MetricsService::StartExternalMetrics() { | |
| 2046 external_metrics_ = new chromeos::ExternalMetrics; | |
| 2047 external_metrics_->Start(); | |
| 2048 } | |
| 2049 #endif | |
| 2050 | |
| 2051 // static | |
| 2052 bool MetricsServiceHelper::IsMetricsReportingEnabled() { | |
| 2053 bool result = false; | |
| 2054 const PrefService* local_state = g_browser_process->local_state(); | |
| 2055 if (local_state) { | |
| 2056 const PrefService::Preference* uma_pref = | |
| 2057 local_state->FindPreference(prefs::kMetricsReportingEnabled); | |
| 2058 if (uma_pref) { | |
| 2059 bool success = uma_pref->GetValue()->GetAsBoolean(&result); | |
| 2060 DCHECK(success); | |
| 2061 } | |
| 2062 } | |
| 2063 return result; | |
| 2064 } | |
| 2065 | |
| 2066 bool MetricsServiceHelper::IsCrashReportingEnabled() { | |
| 2067 #if defined(GOOGLE_CHROME_BUILD) | |
| 2068 #if defined(OS_CHROMEOS) | |
| 2069 bool reporting_enabled = false; | |
| 2070 chromeos::CrosSettings::Get()->GetBoolean(chromeos::kStatsReportingPref, | |
| 2071 &reporting_enabled); | |
| 2072 return reporting_enabled; | |
| 2073 #elif defined(OS_ANDROID) | |
| 2074 // Android has its own settings for metrics / crash uploading. | |
| 2075 const PrefService* prefs = g_browser_process->local_state(); | |
| 2076 return prefs->GetBoolean(prefs::kCrashReportingEnabled); | |
| 2077 #else | |
| 2078 return MetricsServiceHelper::IsMetricsReportingEnabled(); | |
| 2079 #endif | |
| 2080 #else | |
| 2081 return false; | |
| 2082 #endif | |
| 2083 } | |
| OLD | NEW |