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 | |
331 // histograms (which expect non-negative numbers less than MAX_INT). | |
332 int MapCrashExitCodeForHistogram(int exit_code) { | |
333 #if defined(OS_WIN) | |
334 // Since |abs(STATUS_GUARD_PAGE_VIOLATION) == MAX_INT| it causes problems in | |
335 // histograms.cc. Solve this by remapping it to a smaller value, which | |
336 // hopefully doesn't conflict with other codes. | |
337 if (exit_code == STATUS_GUARD_PAGE_VIOLATION) | |
338 return 0x1FCF7EC3; // Randomly picked number. | |
339 #endif | |
340 | |
341 return std::abs(exit_code); | |
342 } | |
343 | |
344 void MarkAppCleanShutdownAndCommit() { | |
345 PrefService* pref = g_browser_process->local_state(); | |
346 pref->SetBoolean(prefs::kStabilityExitedCleanly, true); | |
347 pref->SetInteger(prefs::kStabilityExecutionPhase, | |
348 MetricsService::SHUTDOWN_COMPLETE); | |
349 // Start writing right away (write happens on a different thread). | |
350 pref->CommitPendingWrite(); | |
351 } | |
352 | |
353 } // namespace | 41 } // namespace |
354 | 42 |
355 | 43 MetricsStateManager::MetricsStateManager(PrefService* local_state) |
356 SyntheticTrialGroup::SyntheticTrialGroup(uint32 trial, uint32 group) { | 44 : local_state_(local_state), |
357 id.name = trial; | 45 low_entropy_source_(kLowEntropySourceNotSet), |
358 id.group = group; | 46 entropy_source_returned_(ENTROPY_SOURCE_NONE) { |
47 ResetMetricsIDsIfNecessary(); | |
48 if (IsMetricsReportingEnabled()) | |
49 ForceClientIdCreation(); | |
359 } | 50 } |
360 | 51 |
361 SyntheticTrialGroup::~SyntheticTrialGroup() { | 52 MetricsStateManager::~MetricsStateManager() { |
362 } | 53 } |
363 | 54 |
364 // static | 55 bool MetricsStateManager::IsMetricsReportingEnabled() { |
365 MetricsService::ShutdownCleanliness MetricsService::clean_shutdown_status_ = | 56 // If the user permits metrics reporting with the checkbox in the |
366 MetricsService::CLEANLY_SHUTDOWN; | 57 // prefs, we turn on recording. We disable metrics completely for |
58 // non-official builds. This can be forced with a flag. | |
59 const CommandLine* command_line = CommandLine::ForCurrentProcess(); | |
60 if (command_line->HasSwitch(switches::kEnableMetricsReportingForTesting)) | |
61 return true; | |
367 | 62 |
368 MetricsService::ExecutionPhase MetricsService::execution_phase_ = | 63 bool enabled = false; |
Ilya Sherman
2014/05/02 19:35:06
nit: Let's move this down by a few lines, to be de
Alexei Svitkine (slow)
2014/05/02 20:21:32
Done.
| |
369 MetricsService::UNINITIALIZED_PHASE; | 64 // Disable metrics reporting when field trials are forced. |
65 if (command_line->HasSwitch(switches::kForceFieldTrials)) | |
66 return false; | |
370 | 67 |
371 // This is used to quickly log stats from child process related notifications in | 68 #if defined(GOOGLE_CHROME_BUILD) |
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) | 69 #if defined(OS_CHROMEOS) |
461 registry->RegisterIntegerPref(prefs::kStabilityOtherUserCrashCount, 0); | 70 chromeos::CrosSettings::Get()->GetBoolean(chromeos::kStatsReportingPref, |
462 registry->RegisterIntegerPref(prefs::kStabilityKernelCrashCount, 0); | 71 &enabled); |
463 registry->RegisterIntegerPref(prefs::kStabilitySystemUncleanShutdownCount, 0); | 72 #else |
464 #endif // OS_CHROMEOS | 73 enabled = local_state->GetBoolean(prefs::kMetricsReportingEnabled); |
465 | 74 #endif // #if defined(OS_CHROMEOS) |
466 registry->RegisterStringPref(prefs::kStabilitySavedSystemProfile, | 75 #endif // defined(GOOGLE_CHROME_BUILD) |
467 std::string()); | 76 return enabled; |
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 } | 77 } |
490 | 78 |
491 MetricsService::MetricsService() | 79 void MetricsStateManager::ForceClientIdCreation() { |
492 : metrics_ids_reset_check_performed_(false), | 80 if (!client_id_.empty()) |
493 recording_active_(false), | 81 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 | 82 |
509 log_manager_.set_log_serializer(new MetricsLogSerializer); | 83 client_id_ = local_state_->GetString(prefs::kMetricsClientID); |
510 log_manager_.set_max_ongoing_log_store_size(kUploadLogAvoidRetransmitSize); | 84 if (!client_id_.empty()) |
85 return; | |
511 | 86 |
512 BrowserChildProcessObserver::Add(this); | 87 client_id_ = base::GenerateGUID(); |
88 local_state_->SetString(prefs::kMetricsClientID, client_id_); | |
89 | |
90 if (local_state_->GetString(prefs::kMetricsOldClientID).empty()) { | |
91 // Record the timestamp of when the user opted in to UMA. | |
92 local_state_->SetInt64(prefs::kMetricsReportingEnabledTimestamp, | |
93 base::Time::Now().ToTimeT()); | |
94 } else { | |
95 UMA_HISTOGRAM_BOOLEAN("UMA.ClientIdMigrated", true); | |
96 } | |
97 local_state_->ClearPref(prefs::kMetricsOldClientID); | |
513 } | 98 } |
514 | 99 |
515 MetricsService::~MetricsService() { | 100 void MetricsStateManager::CheckForClonedInstall() { |
516 DisableRecording(); | 101 DCHECK(!cloned_install_detector_); |
517 | 102 |
518 BrowserChildProcessObserver::Remove(this); | 103 MachineIdProvider* provider = MachineIdProvider::CreateInstance(); |
519 } | 104 if (!provider) |
105 return; | |
520 | 106 |
521 void MetricsService::InitializeMetricsRecordingState( | 107 cloned_install_detector_.reset(new ClonedInstallDetector(provider)); |
522 ReportingState reporting_state) { | 108 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 } | 109 } |
562 | 110 |
563 scoped_ptr<const base::FieldTrial::EntropyProvider> | 111 scoped_ptr<const base::FieldTrial::EntropyProvider> |
564 MetricsService::CreateEntropyProvider(ReportingState reporting_state) { | 112 MetricsStateManager::CreateEntropyProvider() { |
565 // For metrics reporting-enabled users, we combine the client ID and low | 113 // 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 | 114 // entropy source to get the final entropy source. Otherwise, only use the low |
567 // entropy source. | 115 // entropy source. |
568 // This has two useful properties: | 116 // This has two useful properties: |
569 // 1) It makes the entropy source less identifiable for parties that do not | 117 // 1) It makes the entropy source less identifiable for parties that do not |
570 // know the low entropy source. | 118 // know the low entropy source. |
571 // 2) It makes the final entropy source resettable. | 119 // 2) It makes the final entropy source resettable. |
572 const int low_entropy_source_value = GetLowEntropySource(); | 120 const int low_entropy_source_value = GetLowEntropySource(); |
573 UMA_HISTOGRAM_SPARSE_SLOWLY("UMA.LowEntropySourceValue", | 121 UMA_HISTOGRAM_SPARSE_SLOWLY("UMA.LowEntropySourceValue", |
574 low_entropy_source_value); | 122 low_entropy_source_value); |
575 if (reporting_state == REPORTING_ENABLED) { | 123 if (IsMetricsReportingEnabled()) { |
576 if (entropy_source_returned_ == LAST_ENTROPY_NONE) | 124 if (entropy_source_returned_ == ENTROPY_SOURCE_NONE) |
577 entropy_source_returned_ = LAST_ENTROPY_HIGH; | 125 entropy_source_returned_ = ENTROPY_SOURCE_HIGH; |
578 DCHECK_EQ(LAST_ENTROPY_HIGH, entropy_source_returned_); | 126 DCHECK_EQ(ENTROPY_SOURCE_HIGH, entropy_source_returned_); |
579 const std::string high_entropy_source = | 127 const std::string high_entropy_source = |
580 client_id_ + base::IntToString(low_entropy_source_value); | 128 client_id_ + base::IntToString(low_entropy_source_value); |
581 return scoped_ptr<const base::FieldTrial::EntropyProvider>( | 129 return scoped_ptr<const base::FieldTrial::EntropyProvider>( |
582 new metrics::SHA1EntropyProvider(high_entropy_source)); | 130 new SHA1EntropyProvider(high_entropy_source)); |
583 } | 131 } |
584 | 132 |
585 if (entropy_source_returned_ == LAST_ENTROPY_NONE) | 133 if (entropy_source_returned_ == ENTROPY_SOURCE_NONE) |
586 entropy_source_returned_ = LAST_ENTROPY_LOW; | 134 entropy_source_returned_ = ENTROPY_SOURCE_LOW; |
587 DCHECK_EQ(LAST_ENTROPY_LOW, entropy_source_returned_); | 135 DCHECK_EQ(ENTROPY_SOURCE_LOW, entropy_source_returned_); |
588 | 136 |
589 #if defined(OS_ANDROID) || defined(OS_IOS) | 137 #if defined(OS_ANDROID) || defined(OS_IOS) |
590 return scoped_ptr<const base::FieldTrial::EntropyProvider>( | 138 return scoped_ptr<const base::FieldTrial::EntropyProvider>( |
591 new metrics::CachingPermutedEntropyProvider( | 139 new CachingPermutedEntropyProvider(local_state_, |
592 g_browser_process->local_state(), | 140 low_entropy_source_value, |
593 low_entropy_source_value, | 141 kMaxLowEntropySize)); |
594 kMaxLowEntropySize)); | |
595 #else | 142 #else |
596 return scoped_ptr<const base::FieldTrial::EntropyProvider>( | 143 return scoped_ptr<const base::FieldTrial::EntropyProvider>( |
597 new metrics::PermutedEntropyProvider(low_entropy_source_value, | 144 new PermutedEntropyProvider(low_entropy_source_value, |
598 kMaxLowEntropySize)); | 145 kMaxLowEntropySize)); |
599 #endif | 146 #endif |
600 } | 147 } |
601 | 148 |
602 void MetricsService::ForceClientIdCreation() { | 149 // static |
603 if (!client_id_.empty()) | 150 void MetricsStateManager::RegisterPrefs(PrefRegistrySimple* registry) { |
604 return; | 151 registry->RegisterBooleanPref(prefs::kMetricsResetIds, false); |
152 registry->RegisterStringPref(prefs::kMetricsClientID, std::string()); | |
153 registry->RegisterInt64Pref(prefs::kMetricsReportingEnabledTimestamp, 0); | |
154 registry->RegisterIntegerPref(prefs::kMetricsLowEntropySource, | |
155 kLowEntropySourceNotSet); | |
605 | 156 |
606 ResetMetricsIDsIfNecessary(); | 157 ClonedInstallDetector::RegisterPrefs(registry); |
158 CachingPermutedEntropyProvider::RegisterPrefs(registry); | |
607 | 159 |
608 PrefService* pref = g_browser_process->local_state(); | 160 // TODO(asvitkine): Remove these once a couple of releases have passed. |
609 client_id_ = pref->GetString(prefs::kMetricsClientID); | 161 // http://crbug.com/357704 |
610 if (!client_id_.empty()) | 162 registry->RegisterStringPref(prefs::kMetricsOldClientID, std::string()); |
611 return; | 163 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 } | 164 } |
625 | 165 |
626 void MetricsService::EnableRecording() { | 166 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 | 167 // 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 | 168 // value are both kLowEntropySourceNotSet, which is used to identify if the |
1178 // value has been set or not. | 169 // value has been set or not. |
1179 if (low_entropy_source_ != kLowEntropySourceNotSet) | 170 if (low_entropy_source_ != kLowEntropySourceNotSet) |
1180 return low_entropy_source_; | 171 return low_entropy_source_; |
1181 | 172 |
1182 ResetMetricsIDsIfNecessary(); | |
1183 | |
1184 PrefService* local_state = g_browser_process->local_state(); | |
1185 const CommandLine* command_line(CommandLine::ForCurrentProcess()); | 173 const CommandLine* command_line(CommandLine::ForCurrentProcess()); |
1186 // Only try to load the value from prefs if the user did not request a reset. | 174 // Only try to load the value from prefs if the user did not request a reset. |
1187 // Otherwise, skip to generating a new value. | 175 // Otherwise, skip to generating a new value. |
1188 if (!command_line->HasSwitch(switches::kResetVariationState)) { | 176 if (!command_line->HasSwitch(switches::kResetVariationState)) { |
1189 int value = local_state->GetInteger(prefs::kMetricsLowEntropySource); | 177 int value = local_state_->GetInteger(prefs::kMetricsLowEntropySource); |
1190 // If the value is outside the [0, kMaxLowEntropySize) range, re-generate | 178 // If the value is outside the [0, kMaxLowEntropySize) range, re-generate |
1191 // it below. | 179 // it below. |
1192 if (value >= 0 && value < kMaxLowEntropySize) { | 180 if (value >= 0 && value < kMaxLowEntropySize) { |
1193 low_entropy_source_ = value; | 181 low_entropy_source_ = value; |
1194 UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", false); | 182 UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", false); |
1195 return low_entropy_source_; | 183 return low_entropy_source_; |
1196 } | 184 } |
1197 } | 185 } |
1198 | 186 |
1199 UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", true); | 187 UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", true); |
1200 low_entropy_source_ = GenerateLowEntropySource(); | 188 low_entropy_source_ = GenerateLowEntropySource(); |
1201 local_state->SetInteger(prefs::kMetricsLowEntropySource, low_entropy_source_); | 189 local_state_->SetInteger(prefs::kMetricsLowEntropySource, |
1202 local_state->ClearPref(prefs::kMetricsOldLowEntropySource); | 190 low_entropy_source_); |
1203 metrics::CachingPermutedEntropyProvider::ClearCache(local_state); | 191 local_state_->ClearPref(prefs::kMetricsOldLowEntropySource); |
192 metrics::CachingPermutedEntropyProvider::ClearCache(local_state_); | |
1204 | 193 |
1205 return low_entropy_source_; | 194 return low_entropy_source_; |
1206 } | 195 } |
1207 | 196 |
1208 // static | 197 void MetricsStateManager::ResetMetricsIDsIfNecessary() { |
1209 std::string MetricsService::GenerateClientID() { | 198 if (!local_state_->GetBoolean(prefs::kMetricsResetIds)) |
1210 return base::GenerateGUID(); | 199 return; |
200 | |
201 UMA_HISTOGRAM_BOOLEAN("UMA.MetricsIDsReset", true); | |
202 | |
203 DCHECK(client_id_.empty()); | |
204 DCHECK_EQ(kLowEntropySourceNotSet, low_entropy_source_); | |
205 | |
206 local_state_->ClearPref(prefs::kMetricsClientID); | |
207 local_state_->ClearPref(prefs::kMetricsLowEntropySource); | |
208 local_state_->ClearPref(prefs::kMetricsResetIds); | |
1211 } | 209 } |
1212 | 210 |
1213 //------------------------------------------------------------------------------ | 211 } // 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 |