blob: 94422a397987401d6c429d6f556c95d22d0e3f7f [file] [log] [blame]
[email protected]2e4cd1a2012-01-12 08:51:031// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
initial.commit09911bf2008-07-26 23:55:295//------------------------------------------------------------------------------
6// Description of the life cycle of a instance of MetricsService.
7//
8// OVERVIEW
9//
[email protected]e3eb0c42013-04-18 06:18:5810// A MetricsService instance is typically created at application startup. It is
11// the central controller for the acquisition of log data, and the automatic
initial.commit09911bf2008-07-26 23:55:2912// 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,
[email protected]e3eb0c42013-04-18 06:18:5816// 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.
initial.commit09911bf2008-07-26 23:55:2923//
[email protected]281d2882009-01-20 20:32:4224// 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 the chromium
initial.commit09911bf2008-07-26 23:55:2926// product (from startup, to browser shutdown). An initial log is generally
27// transmitted some short time (1 minute?) after startup, and includes stats
28// such as recent crash info, the number and types of plugins, etc. The
[email protected]281d2882009-01-20 20:32:4229// external server's response to the initial log conceptually tells this MS if
30// it should continue transmitting logs (during this session). The server
31// response can actually be much more detailed, and always includes (at a
32// minimum) how often additional ongoing logs should be sent.
initial.commit09911bf2008-07-26 23:55:2933//
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)
[email protected]0b33f80b2008-12-17 21:34:3639// is 20 minutes. That time period starts when the first user action causes a
initial.commit09911bf2008-07-26 23:55:2940// logging event. This means that if there is no user action, there may be long
[email protected]281d2882009-01-20 20:32:4241// periods without any (ongoing) log transmissions. Ongoing logs typically
initial.commit09911bf2008-07-26 23:55:2942// 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
[email protected]281d2882009-01-20 20:32:4248// to the UMA server. The finalization also acquires a the most recent number
49// of page loads, along with any counts of renderer or plugin crashes.
initial.commit09911bf2008-07-26 23:55:2950//
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
[email protected]cac267c2011-09-29 15:18:1053// is closed (including snapshotting histograms), and persisted, for
initial.commit09911bf2008-07-26 23:55:2954// potential 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// stats, based on the State enum specified in the state_ member. Those states
77// are:
78//
79// INITIALIZED, // Constructor was called.
[email protected]85ed9d42010-06-08 22:37:4480// INIT_TASK_SCHEDULED, // Waiting for deferred init tasks to complete.
81// INIT_TASK_DONE, // Waiting for timer to send initial log.
initial.commit09911bf2008-07-26 23:55:2982// INITIAL_LOG_READY, // Initial log generated, and waiting for reply.
initial.commit09911bf2008-07-26 23:55:2983// SENDING_OLD_LOGS, // Sending unsent logs from previous session.
84// SENDING_CURRENT_LOGS, // Sending standard current logs as they accrue.
85//
86// In more detail, we have:
87//
88// INITIALIZED, // Constructor was called.
89// The MS has been constructed, but has taken no actions to compose the
90// initial log.
91//
[email protected]85ed9d42010-06-08 22:37:4492// INIT_TASK_SCHEDULED, // Waiting for deferred init tasks to complete.
initial.commit09911bf2008-07-26 23:55:2993// Typically about 30 seconds after startup, a task is sent to a second thread
[email protected]85ed9d42010-06-08 22:37:4494// (the file thread) to perform deferred (lower priority and slower)
95// initialization steps such as getting the list of plugins. That task will
96// (when complete) make an async callback (via a Task) to indicate the
97// completion.
initial.commit09911bf2008-07-26 23:55:2998//
[email protected]85ed9d42010-06-08 22:37:4499// INIT_TASK_DONE, // Waiting for timer to send initial log.
initial.commit09911bf2008-07-26 23:55:29100// The callback has arrived, and it is now possible for an initial log to be
101// created. This callback typically arrives back less than one second after
[email protected]85ed9d42010-06-08 22:37:44102// the deferred init task is dispatched.
initial.commit09911bf2008-07-26 23:55:29103//
104// INITIAL_LOG_READY, // Initial log generated, and waiting for reply.
105// This state is entered only after an initial log has been composed, and
106// prepared for transmission. It is also the case that any previously unsent
107// logs have been loaded into instance variables for possible transmission.
108//
initial.commit09911bf2008-07-26 23:55:29109// SENDING_OLD_LOGS, // Sending unsent logs from previous session.
[email protected]cac267c2011-09-29 15:18:10110// This state indicates that the initial log for this session has been
111// successfully sent and it is now time to send any logs that were
112// saved from previous sessions. All such logs will be transmitted before
113// exiting this state, and proceeding with ongoing logs from the current session
114// (see next state).
initial.commit09911bf2008-07-26 23:55:29115//
116// SENDING_CURRENT_LOGS, // Sending standard current logs as they accrue.
[email protected]0b33f80b2008-12-17 21:34:36117// Current logs are being accumulated. Typically every 20 minutes a log is
initial.commit09911bf2008-07-26 23:55:29118// closed and finalized for transmission, at the same time as a new log is
119// started.
120//
121// The progression through the above states is simple, and sequential, in the
122// most common use cases. States proceed from INITIAL to SENDING_CURRENT_LOGS,
123// and remain in the latter until shutdown.
124//
125// The one unusual case is when the user asks that we stop logging. When that
[email protected]cac267c2011-09-29 15:18:10126// happens, any staged (transmission in progress) log is persisted, and any log
[email protected]410938e02012-10-24 16:33:59127// that is currently accumulating is also finalized and persisted. We then
[email protected]cac267c2011-09-29 15:18:10128// regress back to the SEND_OLD_LOGS state in case the user enables log
129// recording again during this session. This way anything we have persisted
130// will be sent automatically if/when we progress back to SENDING_CURRENT_LOG
131// state.
initial.commit09911bf2008-07-26 23:55:29132//
[email protected]410938e02012-10-24 16:33:59133// Another similar case is on mobile, when the application is backgrounded and
134// then foregrounded again. Backgrounding created new "old" stored logs, so the
135// state drops back from SENDING_CURRENT_LOGS to SENDING_OLD_LOGS so those logs
136// will be sent.
137//
[email protected]cac267c2011-09-29 15:18:10138// Also note that whenever we successfully send an old log, we mirror the list
139// of logs into the PrefService. This ensures that IF we crash, we won't start
140// up and retransmit our old logs again.
initial.commit09911bf2008-07-26 23:55:29141//
142// Due to race conditions, it is always possible that a log file could be sent
143// twice. For example, if a log file is sent, but not yet acknowledged by
144// the external server, and the user shuts down, then a copy of the log may be
145// saved for re-transmission. These duplicates could be filtered out server
[email protected]281d2882009-01-20 20:32:42146// side, but are not expected to be a significant problem.
initial.commit09911bf2008-07-26 23:55:29147//
148//
149//------------------------------------------------------------------------------
150
[email protected]40bcc302009-03-02 20:50:39151#include "chrome/browser/metrics/metrics_service.h"
152
[email protected]d7c1fa62012-06-15 23:35:30153#include <algorithm>
154
[email protected]7f7f1962011-04-20 15:58:16155#include "base/bind.h"
156#include "base/callback.h"
[email protected]5d91c9e2010-07-28 17:25:28157#include "base/command_line.h"
[email protected]3dc1bc42012-06-19 08:20:53158#include "base/guid.h"
[email protected]46f89e142010-07-19 08:00:42159#include "base/md5.h"
[email protected]835d7c82010-10-14 04:38:38160#include "base/metrics/histogram.h"
[email protected]1026afd2013-03-20 14:28:54161#include "base/metrics/sparse_histogram.h"
[email protected]567d30e2012-07-13 21:48:29162#include "base/metrics/statistics_recorder.h"
[email protected]3853a4c2013-02-11 17:15:57163#include "base/prefs/pref_registry_simple.h"
164#include "base/prefs/pref_service.h"
[email protected]e61003a2012-05-24 17:03:19165#include "base/rand_util.h"
[email protected]3ea1b182013-02-08 22:38:41166#include "base/strings/string_number_conversions.h"
[email protected]ce072a72010-12-31 20:02:16167#include "base/threading/platform_thread.h"
[email protected]b3841c502011-03-09 01:21:31168#include "base/threading/thread.h"
[email protected]3a7b66d2012-04-26 16:34:16169#include "base/threading/thread_restrictions.h"
[email protected]ed0fd002012-04-25 23:10:34170#include "base/tracked_objects.h"
[email protected]440b37b22010-08-30 05:31:40171#include "base/utf_string_conversions.h"
[email protected]679082052010-07-21 21:30:13172#include "base/values.h"
[email protected]19871a92012-06-27 19:50:32173#include "chrome/browser/autocomplete/autocomplete_log.h"
initial.commit09911bf2008-07-26 23:55:29174#include "chrome/browser/browser_process.h"
[email protected]6f371442011-11-09 06:45:46175#include "chrome/browser/extensions/extension_service.h"
176#include "chrome/browser/extensions/process_map.h"
[email protected]b8ddb052012-04-19 02:36:06177#include "chrome/browser/io_thread.h"
[email protected]84c988a2011-04-19 17:56:33178#include "chrome/browser/memory_details.h"
[email protected]679082052010-07-21 21:30:13179#include "chrome/browser/metrics/metrics_log.h"
[email protected]cac267c2011-09-29 15:18:10180#include "chrome/browser/metrics/metrics_log_serializer.h"
[email protected]7f7f1962011-04-20 15:58:16181#include "chrome/browser/metrics/metrics_reporting_scheduler.h"
[email protected]ed0fd002012-04-25 23:10:34182#include "chrome/browser/metrics/tracking_synchronizer.h"
[email protected]adbb3762012-03-09 22:20:08183#include "chrome/browser/net/http_pipelining_compatibility_client.h"
[email protected]d67d1052011-06-09 05:11:41184#include "chrome/browser/net/network_stats.h"
[email protected]f8628c22011-04-05 12:10:18185#include "chrome/browser/prefs/scoped_user_pref_update.h"
[email protected]8ecad5e2010-12-02 21:18:33186#include "chrome/browser/profiles/profile.h"
[email protected]8e5c89a2011-06-07 18:13:33187#include "chrome/browser/search_engines/template_url_service.h"
[email protected]71b73f02011-04-06 15:57:29188#include "chrome/browser/ui/browser_list.h"
[email protected]10b132b02012-07-27 20:46:18189#include "chrome/browser/ui/browser_otr_state.h"
[email protected]157d5472009-11-05 22:31:03190#include "chrome/common/child_process_logging.h"
[email protected]f3b357692013-03-22 05:16:13191#include "chrome/common/chrome_process_type.h"
[email protected]432115822011-07-10 15:52:27192#include "chrome/common/chrome_notification_types.h"
[email protected]d7c1fa62012-06-15 23:35:30193#include "chrome/common/chrome_result_codes.h"
[email protected]92745242009-06-12 16:52:21194#include "chrome/common/chrome_switches.h"
[email protected]20f999b52012-08-24 22:32:59195#include "chrome/common/metrics/entropy_provider.h"
[email protected]2e4cd1a2012-01-12 08:51:03196#include "chrome/common/metrics/metrics_log_manager.h"
[email protected]b4a72d842012-03-22 20:09:09197#include "chrome/common/net/test_server_locations.h"
initial.commit09911bf2008-07-26 23:55:29198#include "chrome/common/pref_names.h"
[email protected]e09ba552009-02-05 03:26:29199#include "chrome/common/render_messages.h"
[email protected]4967f792012-01-20 22:14:40200#include "content/public/browser/child_process_data.h"
[email protected]83ab4a282012-07-12 18:19:45201#include "content/public/browser/histogram_fetcher.h"
[email protected]09d31d52012-03-11 22:30:27202#include "content/public/browser/load_notification_details.h"
[email protected]ad50def52011-10-19 23:17:07203#include "content/public/browser/notification_service.h"
[email protected]3a5180ae2011-12-21 02:39:38204#include "content/public/browser/plugin_service.h"
[email protected]f3b1a082011-11-18 00:34:30205#include "content/public/browser/render_process_host.h"
[email protected]5d490e42012-08-30 05:16:43206#include "content/public/browser/user_metrics.h"
[email protected]459f3502012-09-17 17:08:12207#include "content/public/browser/web_contents.h"
[email protected]fe58acc22012-02-29 01:29:58208#include "net/base/load_flags.h"
[email protected]3dc1bc42012-06-19 08:20:53209#include "net/url_request/url_fetcher.h"
[email protected]91d9f3d2011-08-14 05:24:44210#include "webkit/plugins/webplugininfo.h"
initial.commit09911bf2008-07-26 23:55:29211
[email protected]e06131d2010-02-10 18:40:33212// TODO(port): port browser_distribution.h.
213#if !defined(OS_POSIX)
[email protected]79bf0b72009-04-27 21:30:55214#include "chrome/installer/util/browser_distribution.h"
[email protected]dc6f4962009-02-13 01:25:50215#endif
216
[email protected]5ccaa412009-11-13 22:00:16217#if defined(OS_CHROMEOS)
[email protected]db342d52010-08-09 21:19:37218#include "chrome/browser/chromeos/cros/cros_library.h"
[email protected]5ccaa412009-11-13 22:00:16219#include "chrome/browser/chromeos/external_metrics.h"
[email protected]d43970a72011-07-10 06:24:52220#include "chrome/browser/chromeos/system/statistics_provider.h"
[email protected]5ccaa412009-11-13 22:00:16221#endif
222
[email protected]d7c1fa62012-06-15 23:35:30223#if defined(OS_WIN)
224#include <windows.h> // Needed for STATUS_* codes
[email protected]d7c1fa62012-06-15 23:35:30225#endif
226
[email protected]e1acf6f2008-10-27 20:43:33227using base::Time;
[email protected]631bb742011-11-02 11:29:39228using content::BrowserThread;
[email protected]4967f792012-01-20 22:14:40229using content::ChildProcessData;
[email protected]09d31d52012-03-11 22:30:27230using content::LoadNotificationDetails;
[email protected]3a5180ae2011-12-21 02:39:38231using content::PluginService;
[email protected]e1acf6f2008-10-27 20:43:33232
[email protected]fe58acc22012-02-29 01:29:58233namespace {
[email protected]b2a4812d2012-02-28 05:31:31234
[email protected]fe58acc22012-02-29 01:29:58235// Check to see that we're being called on only one thread.
236bool IsSingleThreaded() {
237 static base::PlatformThreadId thread_id = 0;
238 if (!thread_id)
239 thread_id = base::PlatformThread::CurrentId();
240 return base::PlatformThread::CurrentId() == thread_id;
241}
242
[email protected]7f7f1962011-04-20 15:58:16243// The delay, in seconds, after starting recording before doing expensive
244// initialization work.
[email protected]12180f82012-10-10 21:13:30245#if defined(OS_ANDROID) || defined(OS_IOS)
246// On mobile devices, a significant portion of sessions last less than a minute.
247// Use a shorter timer on these platforms to avoid losing data.
248// TODO(dfalcantara): To avoid delaying startup, tighten up initialization so
249// that it occurs after the user gets their initial page.
250const int kInitializationDelaySeconds = 5;
251#else
[email protected]fe58acc22012-02-29 01:29:58252const int kInitializationDelaySeconds = 30;
[email protected]12180f82012-10-10 21:13:30253#endif
[email protected]252873ef2008-08-04 21:59:45254
[email protected]c9a3ef82009-05-28 22:02:46255// This specifies the amount of time to wait for all renderers to send their
256// data.
[email protected]fe58acc22012-02-29 01:29:58257const int kMaxHistogramGatheringWaitDuration = 60000; // 60 seconds.
[email protected]c9a3ef82009-05-28 22:02:46258
[email protected]54702c92011-04-15 15:06:43259// The maximum number of events in a log uploaded to the UMA server.
[email protected]fe58acc22012-02-29 01:29:58260const int kEventLimit = 2400;
[email protected]68475e602008-08-22 03:21:15261
262// If an upload fails, and the transmission was over this byte count, then we
263// will discard the log, and not try to retransmit it. We also don't persist
264// the log to the prefs for transmission during the next chrome session if this
265// limit is exceeded.
[email protected]fe58acc22012-02-29 01:29:58266const size_t kUploadLogAvoidRetransmitSize = 50000;
initial.commit09911bf2008-07-26 23:55:29267
[email protected]fc4252a72012-01-12 21:58:47268// Interval, in minutes, between state saves.
[email protected]fe58acc22012-02-29 01:29:58269const int kSaveStateIntervalMinutes = 5;
270
[email protected]4266def22012-05-17 01:02:40271enum ResponseStatus {
272 UNKNOWN_FAILURE,
273 SUCCESS,
274 BAD_REQUEST, // Invalid syntax or log too large.
[email protected]9f5c1ce82012-05-23 23:11:28275 NO_RESPONSE,
[email protected]4266def22012-05-17 01:02:40276 NUM_RESPONSE_STATUSES
277};
278
279ResponseStatus ResponseCodeToStatus(int response_code) {
280 switch (response_code) {
281 case 200:
282 return SUCCESS;
283 case 400:
284 return BAD_REQUEST;
[email protected]9f5c1ce82012-05-23 23:11:28285 case net::URLFetcher::RESPONSE_CODE_INVALID:
286 return NO_RESPONSE;
[email protected]4266def22012-05-17 01:02:40287 default:
288 return UNKNOWN_FAILURE;
289 }
290}
291
[email protected]e61003a2012-05-24 17:03:19292// The argument used to generate a non-identifying entropy source. We want no
293// more than 13 bits of entropy, so use this max to return a number between 1
294// and 2^13 = 8192 as the entropy source.
[email protected]20f999b52012-08-24 22:32:59295const uint32 kMaxLowEntropySize = (1 << 13);
[email protected]e61003a2012-05-24 17:03:19296
[email protected]e63a9ef2012-08-30 15:29:42297// Default prefs value for prefs::kMetricsLowEntropySource to indicate that the
298// value has not yet been set.
299const int kLowEntropySourceNotSet = -1;
300
[email protected]e61003a2012-05-24 17:03:19301// Generates a new non-identifying entropy source used to seed persistent
302// activities.
303int GenerateLowEntropySource() {
[email protected]20f999b52012-08-24 22:32:59304 return base::RandInt(0, kMaxLowEntropySize - 1);
[email protected]e61003a2012-05-24 17:03:19305}
306
[email protected]d7c1fa62012-06-15 23:35:30307// Converts an exit code into something that can be inserted into our
308// histograms (which expect non-negative numbers less than MAX_INT).
309int MapCrashExitCodeForHistogram(int exit_code) {
310#if defined(OS_WIN)
311 // Since |abs(STATUS_GUARD_PAGE_VIOLATION) == MAX_INT| it causes problems in
312 // histograms.cc. Solve this by remapping it to a smaller value, which
313 // hopefully doesn't conflict with other codes.
314 if (exit_code == STATUS_GUARD_PAGE_VIOLATION)
315 return 0x1FCF7EC3; // Randomly picked number.
316#endif
317
318 return std::abs(exit_code);
319}
320
[email protected]84c384e2013-03-01 23:20:19321void MarkAppCleanShutdownAndCommit() {
322 PrefService* pref = g_browser_process->local_state();
323 pref->SetBoolean(prefs::kStabilityExitedCleanly, true);
324 // Start writing right away (write happens on a different thread).
325 pref->CommitPendingWrite();
326}
327
[email protected]20f999b52012-08-24 22:32:59328} // namespace
initial.commit09911bf2008-07-26 23:55:29329
[email protected]c0c55e92011-09-10 18:47:30330// static
331MetricsService::ShutdownCleanliness MetricsService::clean_shutdown_status_ =
332 MetricsService::CLEANLY_SHUTDOWN;
333
[email protected]679082052010-07-21 21:30:13334// This is used to quickly log stats from child process related notifications in
335// MetricsService::child_stats_buffer_. The buffer's contents are transferred
336// out when Local State is periodically saved. The information is then
337// reported to the UMA server on next launch.
338struct MetricsService::ChildProcessStats {
339 public:
[email protected]f3b357692013-03-22 05:16:13340 explicit ChildProcessStats(int process_type)
[email protected]679082052010-07-21 21:30:13341 : process_launches(0),
342 process_crashes(0),
343 instances(0),
[email protected]cd937072012-07-02 09:00:29344 loading_errors(0),
[email protected]f3b357692013-03-22 05:16:13345 process_type(process_type) {}
[email protected]679082052010-07-21 21:30:13346
347 // This constructor is only used by the map to return some default value for
348 // an index for which no value has been assigned.
349 ChildProcessStats()
350 : process_launches(0),
[email protected]d88bf0a2011-08-30 23:55:57351 process_crashes(0),
352 instances(0),
[email protected]cd937072012-07-02 09:00:29353 loading_errors(0),
[email protected]bd5d6cf2011-12-01 00:39:12354 process_type(content::PROCESS_TYPE_UNKNOWN) {}
[email protected]679082052010-07-21 21:30:13355
356 // The number of times that the given child process has been launched
357 int process_launches;
358
359 // The number of times that the given child process has crashed
360 int process_crashes;
361
362 // The number of instances of this child process that have been created.
363 // An instance is a DOM object rendered by this child process during a page
364 // load.
365 int instances;
366
[email protected]cd937072012-07-02 09:00:29367 // The number of times there was an error loading an instance of this child
368 // process.
369 int loading_errors;
370
[email protected]f3b357692013-03-22 05:16:13371 int process_type;
[email protected]679082052010-07-21 21:30:13372};
initial.commit09911bf2008-07-26 23:55:29373
[email protected]84c988a2011-04-19 17:56:33374// Handles asynchronous fetching of memory details.
375// Will run the provided task after finished.
376class MetricsMemoryDetails : public MemoryDetails {
377 public:
[email protected]2226c222011-11-22 00:08:40378 explicit MetricsMemoryDetails(const base::Closure& callback)
379 : callback_(callback) {}
[email protected]84c988a2011-04-19 17:56:33380
[email protected]b94584a2013-02-07 03:02:08381 virtual void OnDetailsAvailable() OVERRIDE {
[email protected]2226c222011-11-22 00:08:40382 MessageLoop::current()->PostTask(FROM_HERE, callback_);
[email protected]84c988a2011-04-19 17:56:33383 }
384
385 private:
[email protected]b94584a2013-02-07 03:02:08386 virtual ~MetricsMemoryDetails() {}
[email protected]84c988a2011-04-19 17:56:33387
[email protected]2226c222011-11-22 00:08:40388 base::Closure callback_;
[email protected]84c988a2011-04-19 17:56:33389 DISALLOW_COPY_AND_ASSIGN(MetricsMemoryDetails);
390};
391
initial.commit09911bf2008-07-26 23:55:29392// static
[email protected]b1de2c72013-02-06 02:45:47393void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
initial.commit09911bf2008-07-26 23:55:29394 DCHECK(IsSingleThreaded());
[email protected]007b3f82013-04-09 08:46:45395 registry->RegisterStringPref(prefs::kMetricsClientID, std::string());
[email protected]b1de2c72013-02-06 02:45:47396 registry->RegisterIntegerPref(prefs::kMetricsLowEntropySource,
397 kLowEntropySourceNotSet);
398 registry->RegisterInt64Pref(prefs::kMetricsClientIDTimestamp, 0);
399 registry->RegisterInt64Pref(prefs::kStabilityLaunchTimeSec, 0);
400 registry->RegisterInt64Pref(prefs::kStabilityLastTimestampSec, 0);
[email protected]007b3f82013-04-09 08:46:45401 registry->RegisterStringPref(prefs::kStabilityStatsVersion, std::string());
[email protected]b1de2c72013-02-06 02:45:47402 registry->RegisterInt64Pref(prefs::kStabilityStatsBuildTime, 0);
403 registry->RegisterBooleanPref(prefs::kStabilityExitedCleanly, true);
404 registry->RegisterBooleanPref(prefs::kStabilitySessionEndCompleted, true);
405 registry->RegisterIntegerPref(prefs::kMetricsSessionID, -1);
406 registry->RegisterIntegerPref(prefs::kStabilityLaunchCount, 0);
407 registry->RegisterIntegerPref(prefs::kStabilityCrashCount, 0);
408 registry->RegisterIntegerPref(prefs::kStabilityIncompleteSessionEndCount, 0);
409 registry->RegisterIntegerPref(prefs::kStabilityPageLoadCount, 0);
410 registry->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0);
411 registry->RegisterIntegerPref(prefs::kStabilityExtensionRendererCrashCount,
412 0);
413 registry->RegisterIntegerPref(prefs::kStabilityRendererHangCount, 0);
414 registry->RegisterIntegerPref(prefs::kStabilityChildProcessCrashCount, 0);
415 registry->RegisterIntegerPref(prefs::kStabilityBreakpadRegistrationFail, 0);
416 registry->RegisterIntegerPref(prefs::kStabilityBreakpadRegistrationSuccess,
417 0);
418 registry->RegisterIntegerPref(prefs::kStabilityDebuggerPresent, 0);
419 registry->RegisterIntegerPref(prefs::kStabilityDebuggerNotPresent, 0);
[email protected]c1834a92011-01-21 18:21:03420#if defined(OS_CHROMEOS)
[email protected]b1de2c72013-02-06 02:45:47421 registry->RegisterIntegerPref(prefs::kStabilityOtherUserCrashCount, 0);
422 registry->RegisterIntegerPref(prefs::kStabilityKernelCrashCount, 0);
423 registry->RegisterIntegerPref(prefs::kStabilitySystemUncleanShutdownCount, 0);
[email protected]c1834a92011-01-21 18:21:03424#endif // OS_CHROMEOS
[email protected]e73c01972008-08-13 00:18:24425
[email protected]b1de2c72013-02-06 02:45:47426 registry->RegisterDictionaryPref(prefs::kProfileMetrics);
427 registry->RegisterIntegerPref(prefs::kNumKeywords, 0);
428 registry->RegisterListPref(prefs::kMetricsInitialLogsXml);
429 registry->RegisterListPref(prefs::kMetricsOngoingLogsXml);
430 registry->RegisterListPref(prefs::kMetricsInitialLogsProto);
431 registry->RegisterListPref(prefs::kMetricsOngoingLogsProto);
[email protected]0bb1a622009-03-04 03:22:32432
[email protected]5c181552013-02-07 09:12:52433 registry->RegisterInt64Pref(prefs::kInstallDate, 0);
[email protected]b1de2c72013-02-06 02:45:47434 registry->RegisterInt64Pref(prefs::kUninstallMetricsPageLoadCount, 0);
435 registry->RegisterInt64Pref(prefs::kUninstallLaunchCount, 0);
[email protected]b1de2c72013-02-06 02:45:47436 registry->RegisterInt64Pref(prefs::kUninstallMetricsUptimeSec, 0);
437 registry->RegisterInt64Pref(prefs::kUninstallLastLaunchTimeSec, 0);
438 registry->RegisterInt64Pref(prefs::kUninstallLastObservedRunTimeSec, 0);
initial.commit09911bf2008-07-26 23:55:29439}
440
[email protected]541f77922009-02-23 21:14:38441// static
442void MetricsService::DiscardOldStabilityStats(PrefService* local_state) {
443 local_state->SetBoolean(prefs::kStabilityExitedCleanly, true);
[email protected]c9abf242009-07-18 06:00:38444 local_state->SetBoolean(prefs::kStabilitySessionEndCompleted, true);
[email protected]541f77922009-02-23 21:14:38445
446 local_state->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0);
447 local_state->SetInteger(prefs::kStabilityBreakpadRegistrationSuccess, 0);
448 local_state->SetInteger(prefs::kStabilityBreakpadRegistrationFail, 0);
449 local_state->SetInteger(prefs::kStabilityDebuggerPresent, 0);
450 local_state->SetInteger(prefs::kStabilityDebuggerNotPresent, 0);
451
452 local_state->SetInteger(prefs::kStabilityLaunchCount, 0);
453 local_state->SetInteger(prefs::kStabilityCrashCount, 0);
454
455 local_state->SetInteger(prefs::kStabilityPageLoadCount, 0);
456 local_state->SetInteger(prefs::kStabilityRendererCrashCount, 0);
457 local_state->SetInteger(prefs::kStabilityRendererHangCount, 0);
458
[email protected]9165f742010-03-10 22:55:01459 local_state->SetInt64(prefs::kStabilityLaunchTimeSec, 0);
460 local_state->SetInt64(prefs::kStabilityLastTimestampSec, 0);
[email protected]541f77922009-02-23 21:14:38461
462 local_state->ClearPref(prefs::kStabilityPluginStats);
[email protected]ae155cb92009-06-19 06:10:37463
[email protected]fe58acc22012-02-29 01:29:58464 local_state->ClearPref(prefs::kMetricsInitialLogsXml);
465 local_state->ClearPref(prefs::kMetricsOngoingLogsXml);
466 local_state->ClearPref(prefs::kMetricsInitialLogsProto);
467 local_state->ClearPref(prefs::kMetricsOngoingLogsProto);
[email protected]541f77922009-02-23 21:14:38468}
469
initial.commit09911bf2008-07-26 23:55:29470MetricsService::MetricsService()
[email protected]d01b8732008-10-16 02:18:07471 : recording_active_(false),
472 reporting_active_(false),
[email protected]410938e02012-10-24 16:33:59473 test_mode_active_(false),
[email protected]d01b8732008-10-16 02:18:07474 state_(INITIALIZED),
[email protected]e88be472e2013-04-26 22:36:36475 low_entropy_source_(kLowEntropySourceNotSet),
[email protected]d01b8732008-10-16 02:18:07476 idle_since_last_transmission_(false),
initial.commit09911bf2008-07-26 23:55:29477 next_window_id_(0),
[email protected]c94d7382012-02-28 08:43:40478 ALLOW_THIS_IN_INITIALIZER_LIST(self_ptr_factory_(this)),
[email protected]40bcc302009-03-02 20:50:39479 ALLOW_THIS_IN_INITIALIZER_LIST(state_saver_factory_(this)),
[email protected]e5354322012-08-09 23:07:37480 waiting_for_asynchronous_reporting_step_(false),
481 entropy_source_returned_(LAST_ENTROPY_NONE) {
initial.commit09911bf2008-07-26 23:55:29482 DCHECK(IsSingleThreaded());
483 InitializeMetricsState();
[email protected]7f7f1962011-04-20 15:58:16484
485 base::Closure callback = base::Bind(&MetricsService::StartScheduledUpload,
[email protected]c94d7382012-02-28 08:43:40486 self_ptr_factory_.GetWeakPtr());
[email protected]7f7f1962011-04-20 15:58:16487 scheduler_.reset(new MetricsReportingScheduler(callback));
[email protected]cac267c2011-09-29 15:18:10488 log_manager_.set_log_serializer(new MetricsLogSerializer());
489 log_manager_.set_max_ongoing_log_store_size(kUploadLogAvoidRetransmitSize);
[email protected]f4eaf7b92013-02-28 22:00:40490
491 BrowserChildProcessObserver::Add(this);
initial.commit09911bf2008-07-26 23:55:29492}
493
494MetricsService::~MetricsService() {
[email protected]410938e02012-10-24 16:33:59495 DisableRecording();
[email protected]f4eaf7b92013-02-28 22:00:40496
497 BrowserChildProcessObserver::Remove(this);
initial.commit09911bf2008-07-26 23:55:29498}
499
[email protected]d01b8732008-10-16 02:18:07500void MetricsService::Start() {
[email protected]b1c8dc02011-04-13 18:32:04501 HandleIdleSinceLastTransmission(false);
[email protected]410938e02012-10-24 16:33:59502 EnableRecording();
503 EnableReporting();
[email protected]d01b8732008-10-16 02:18:07504}
505
[email protected]410938e02012-10-24 16:33:59506void MetricsService::StartRecordingForTests() {
507 test_mode_active_ = true;
508 EnableRecording();
509 DisableReporting();
[email protected]d01b8732008-10-16 02:18:07510}
511
512void MetricsService::Stop() {
[email protected]b1c8dc02011-04-13 18:32:04513 HandleIdleSinceLastTransmission(false);
[email protected]410938e02012-10-24 16:33:59514 DisableReporting();
515 DisableRecording();
516}
517
518void MetricsService::EnableReporting() {
519 if (reporting_active_)
520 return;
521 reporting_active_ = true;
522 StartSchedulerIfNecessary();
523}
524
525void MetricsService::DisableReporting() {
526 reporting_active_ = false;
[email protected]d01b8732008-10-16 02:18:07527}
528
[email protected]edafd4c2011-05-10 17:18:53529std::string MetricsService::GetClientId() {
530 return client_id_;
531}
532
[email protected]20f999b52012-08-24 22:32:59533scoped_ptr<const base::FieldTrial::EntropyProvider>
534 MetricsService::CreateEntropyProvider(bool reporting_will_be_enabled) {
[email protected]29d81ee02012-05-25 05:45:42535 // For metrics reporting-enabled users, we combine the client ID and low
536 // entropy source to get the final entropy source. Otherwise, only use the low
537 // entropy source.
538 // This has two useful properties:
[email protected]e61003a2012-05-24 17:03:19539 // 1) It makes the entropy source less identifiable for parties that do not
540 // know the low entropy source.
541 // 2) It makes the final entropy source resettable.
[email protected]e5354322012-08-09 23:07:37542 if (reporting_will_be_enabled) {
[email protected]5fbffb72012-08-18 01:59:18543 if (entropy_source_returned_ == LAST_ENTROPY_NONE)
544 entropy_source_returned_ = LAST_ENTROPY_HIGH;
545 DCHECK_EQ(LAST_ENTROPY_HIGH, entropy_source_returned_);
[email protected]20f999b52012-08-24 22:32:59546 const std::string high_entropy_source =
547 client_id_ + base::IntToString(GetLowEntropySource());
548 return scoped_ptr<const base::FieldTrial::EntropyProvider>(
549 new metrics::SHA1EntropyProvider(high_entropy_source));
[email protected]e5354322012-08-09 23:07:37550 }
[email protected]20f999b52012-08-24 22:32:59551
[email protected]5fbffb72012-08-18 01:59:18552 if (entropy_source_returned_ == LAST_ENTROPY_NONE)
553 entropy_source_returned_ = LAST_ENTROPY_LOW;
554 DCHECK_EQ(LAST_ENTROPY_LOW, entropy_source_returned_);
[email protected]20f999b52012-08-24 22:32:59555 return scoped_ptr<const base::FieldTrial::EntropyProvider>(
556 new metrics::PermutedEntropyProvider(GetLowEntropySource(),
557 kMaxLowEntropySize));
[email protected]e61003a2012-05-24 17:03:19558}
559
[email protected]5cbeeef72012-02-08 02:05:18560void MetricsService::ForceClientIdCreation() {
561 if (!client_id_.empty())
562 return;
563 PrefService* pref = g_browser_process->local_state();
564 client_id_ = pref->GetString(prefs::kMetricsClientID);
565 if (!client_id_.empty())
566 return;
567
568 client_id_ = GenerateClientID();
569 pref->SetString(prefs::kMetricsClientID, client_id_);
570
571 // Might as well make a note of how long this ID has existed
572 pref->SetString(prefs::kMetricsClientIDTimestamp,
573 base::Int64ToString(Time::Now().ToTimeT()));
574}
575
[email protected]410938e02012-10-24 16:33:59576void MetricsService::EnableRecording() {
initial.commit09911bf2008-07-26 23:55:29577 DCHECK(IsSingleThreaded());
578
[email protected]410938e02012-10-24 16:33:59579 if (recording_active_)
initial.commit09911bf2008-07-26 23:55:29580 return;
[email protected]410938e02012-10-24 16:33:59581 recording_active_ = true;
initial.commit09911bf2008-07-26 23:55:29582
[email protected]410938e02012-10-24 16:33:59583 ForceClientIdCreation();
584 child_process_logging::SetClientId(client_id_);
585 if (!log_manager_.current_log())
586 OpenNewLog();
[email protected]005ef3e2009-05-22 20:55:46587
[email protected]410938e02012-10-24 16:33:59588 SetUpNotifications(&registrar_, this);
[email protected]dd98f392013-02-04 13:03:22589 content::RemoveActionCallback(action_callback_);
590 action_callback_ = base::Bind(&MetricsService::OnUserAction,
591 base::Unretained(this));
592 content::AddActionCallback(action_callback_);
[email protected]410938e02012-10-24 16:33:59593}
594
595void MetricsService::DisableRecording() {
596 DCHECK(IsSingleThreaded());
597
598 if (!recording_active_)
599 return;
600 recording_active_ = false;
601
[email protected]dd98f392013-02-04 13:03:22602 content::RemoveActionCallback(action_callback_);
[email protected]410938e02012-10-24 16:33:59603 registrar_.RemoveAll();
604 PushPendingLogsToPersistentStorage();
605 DCHECK(!log_manager_.has_staged_log());
initial.commit09911bf2008-07-26 23:55:29606}
607
[email protected]d01b8732008-10-16 02:18:07608bool MetricsService::recording_active() const {
initial.commit09911bf2008-07-26 23:55:29609 DCHECK(IsSingleThreaded());
[email protected]d01b8732008-10-16 02:18:07610 return recording_active_;
initial.commit09911bf2008-07-26 23:55:29611}
612
[email protected]d01b8732008-10-16 02:18:07613bool MetricsService::reporting_active() const {
614 DCHECK(IsSingleThreaded());
615 return reporting_active_;
initial.commit09911bf2008-07-26 23:55:29616}
617
[email protected]87ef9ea2011-02-26 03:15:15618// static
[email protected]6c2381d2011-10-19 02:52:53619void MetricsService::SetUpNotifications(
620 content::NotificationRegistrar* registrar,
621 content::NotificationObserver* observer) {
622 registrar->Add(observer, chrome::NOTIFICATION_BROWSER_OPENED,
[email protected]ad50def52011-10-19 23:17:07623 content::NotificationService::AllBrowserContextsAndSources());
[email protected]6c2381d2011-10-19 02:52:53624 registrar->Add(observer, chrome::NOTIFICATION_BROWSER_CLOSED,
[email protected]ad50def52011-10-19 23:17:07625 content::NotificationService::AllSources());
[email protected]884033e2012-04-16 19:38:42626 registrar->Add(observer, chrome::NOTIFICATION_TAB_PARENTED,
[email protected]ad50def52011-10-19 23:17:07627 content::NotificationService::AllSources());
[email protected]884033e2012-04-16 19:38:42628 registrar->Add(observer, chrome::NOTIFICATION_TAB_CLOSING,
[email protected]ad50def52011-10-19 23:17:07629 content::NotificationService::AllSources());
[email protected]6c2381d2011-10-19 02:52:53630 registrar->Add(observer, content::NOTIFICATION_LOAD_START,
[email protected]ad50def52011-10-19 23:17:07631 content::NotificationService::AllSources());
[email protected]6c2381d2011-10-19 02:52:53632 registrar->Add(observer, content::NOTIFICATION_LOAD_STOP,
[email protected]ad50def52011-10-19 23:17:07633 content::NotificationService::AllSources());
[email protected]6c2381d2011-10-19 02:52:53634 registrar->Add(observer, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
[email protected]ad50def52011-10-19 23:17:07635 content::NotificationService::AllSources());
[email protected]6c2381d2011-10-19 02:52:53636 registrar->Add(observer, content::NOTIFICATION_RENDERER_PROCESS_HANG,
[email protected]ad50def52011-10-19 23:17:07637 content::NotificationService::AllSources());
[email protected]6c2381d2011-10-19 02:52:53638 registrar->Add(observer, chrome::NOTIFICATION_TEMPLATE_URL_SERVICE_LOADED,
[email protected]ad50def52011-10-19 23:17:07639 content::NotificationService::AllSources());
[email protected]6c2381d2011-10-19 02:52:53640 registrar->Add(observer, chrome::NOTIFICATION_OMNIBOX_OPENED_URL,
[email protected]ad50def52011-10-19 23:17:07641 content::NotificationService::AllSources());
[email protected]87ef9ea2011-02-26 03:15:15642}
643
[email protected]f4eaf7b92013-02-28 22:00:40644void MetricsService::BrowserChildProcessHostConnected(
645 const content::ChildProcessData& data) {
646 GetChildProcessStats(data).process_launches++;
647}
648
649void MetricsService::BrowserChildProcessCrashed(
650 const content::ChildProcessData& data) {
651 GetChildProcessStats(data).process_crashes++;
652 // Exclude plugin crashes from the count below because we report them via
653 // a separate UMA metric.
[email protected]f3b357692013-03-22 05:16:13654 if (!IsPluginProcess(data.process_type))
[email protected]f4eaf7b92013-02-28 22:00:40655 IncrementPrefValue(prefs::kStabilityChildProcessCrashCount);
656}
657
658void MetricsService::BrowserChildProcessInstanceCreated(
659 const content::ChildProcessData& data) {
660 GetChildProcessStats(data).instances++;
661}
662
[email protected]432115822011-07-10 15:52:27663void MetricsService::Observe(int type,
[email protected]6c2381d2011-10-19 02:52:53664 const content::NotificationSource& source,
665 const content::NotificationDetails& details) {
[email protected]cac267c2011-09-29 15:18:10666 DCHECK(log_manager_.current_log());
initial.commit09911bf2008-07-26 23:55:29667 DCHECK(IsSingleThreaded());
668
[email protected]dd98f392013-02-04 13:03:22669 if (!CanLogNotification())
initial.commit09911bf2008-07-26 23:55:29670 return;
671
[email protected]432115822011-07-10 15:52:27672 switch (type) {
[email protected]432115822011-07-10 15:52:27673 case chrome::NOTIFICATION_BROWSER_OPENED:
[email protected]459f3502012-09-17 17:08:12674 case chrome::NOTIFICATION_BROWSER_CLOSED: {
675 Browser* browser = content::Source<Browser>(source).ptr();
676 LogWindowOrTabChange(type, reinterpret_cast<uintptr_t>(browser));
initial.commit09911bf2008-07-26 23:55:29677 break;
[email protected]459f3502012-09-17 17:08:12678 }
initial.commit09911bf2008-07-26 23:55:29679
[email protected]459f3502012-09-17 17:08:12680 case chrome::NOTIFICATION_TAB_PARENTED: {
681 content::WebContents* web_contents =
682 content::Source<content::WebContents>(source).ptr();
683 LogWindowOrTabChange(type, reinterpret_cast<uintptr_t>(web_contents));
initial.commit09911bf2008-07-26 23:55:29684 break;
[email protected]459f3502012-09-17 17:08:12685 }
686
687 case chrome::NOTIFICATION_TAB_CLOSING: {
688 content::NavigationController* controller =
689 content::Source<content::NavigationController>(source).ptr();
690 content::WebContents* web_contents = controller->GetWebContents();
691 LogWindowOrTabChange(type, reinterpret_cast<uintptr_t>(web_contents));
692 break;
693 }
initial.commit09911bf2008-07-26 23:55:29694
[email protected]432115822011-07-10 15:52:27695 case content::NOTIFICATION_LOAD_STOP:
initial.commit09911bf2008-07-26 23:55:29696 LogLoadComplete(type, source, details);
697 break;
698
[email protected]432115822011-07-10 15:52:27699 case content::NOTIFICATION_LOAD_START:
initial.commit09911bf2008-07-26 23:55:29700 LogLoadStarted();
701 break;
702
[email protected]432115822011-07-10 15:52:27703 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
[email protected]f3b1a082011-11-18 00:34:30704 content::RenderProcessHost::RendererClosedDetails* process_details =
705 content::Details<
706 content::RenderProcessHost::RendererClosedDetails>(
707 details).ptr();
708 content::RenderProcessHost* host =
709 content::Source<content::RenderProcessHost>(source).ptr();
[email protected]c3721482012-03-23 16:21:48710 LogRendererCrash(
[email protected]f1675202012-07-09 15:18:00711 host, process_details->status, process_details->exit_code);
[email protected]1f085622009-12-04 05:33:45712 }
initial.commit09911bf2008-07-26 23:55:29713 break;
714
[email protected]432115822011-07-10 15:52:27715 case content::NOTIFICATION_RENDERER_PROCESS_HANG:
initial.commit09911bf2008-07-26 23:55:29716 LogRendererHang();
717 break;
718
[email protected]432115822011-07-10 15:52:27719 case chrome::NOTIFICATION_TEMPLATE_URL_SERVICE_LOADED:
[email protected]604d8a062012-03-16 21:37:46720 LogKeywordCount(content::Source<TemplateURLService>(
721 source)->GetTemplateURLs().size());
initial.commit09911bf2008-07-26 23:55:29722 break;
723
[email protected]432115822011-07-10 15:52:27724 case chrome::NOTIFICATION_OMNIBOX_OPENED_URL: {
[email protected]279703f2012-01-20 22:23:26725 MetricsLog* current_log =
726 static_cast<MetricsLog*>(log_manager_.current_log());
[email protected]1226abb2010-06-10 18:01:28727 DCHECK(current_log);
728 current_log->RecordOmniboxOpenedURL(
[email protected]6c2381d2011-10-19 02:52:53729 *content::Details<AutocompleteLog>(details).ptr());
initial.commit09911bf2008-07-26 23:55:29730 break;
[email protected]1226abb2010-06-10 18:01:28731 }
initial.commit09911bf2008-07-26 23:55:29732
initial.commit09911bf2008-07-26 23:55:29733 default:
[email protected]a063c102010-07-22 22:20:19734 NOTREACHED();
initial.commit09911bf2008-07-26 23:55:29735 break;
736 }
[email protected]d01b8732008-10-16 02:18:07737
738 HandleIdleSinceLastTransmission(false);
[email protected]d01b8732008-10-16 02:18:07739}
740
741void MetricsService::HandleIdleSinceLastTransmission(bool in_idle) {
742 // If there wasn't a lot of action, maybe the computer was asleep, in which
743 // case, the log transmissions should have stopped. Here we start them up
744 // again.
[email protected]cac78842008-11-27 01:02:20745 if (!in_idle && idle_since_last_transmission_)
[email protected]7f7f1962011-04-20 15:58:16746 StartSchedulerIfNecessary();
[email protected]cac78842008-11-27 01:02:20747 idle_since_last_transmission_ = in_idle;
initial.commit09911bf2008-07-26 23:55:29748}
749
initial.commit09911bf2008-07-26 23:55:29750void MetricsService::RecordStartOfSessionEnd() {
[email protected]466f3c12011-03-23 21:20:38751 LogCleanShutdown();
initial.commit09911bf2008-07-26 23:55:29752 RecordBooleanPrefValue(prefs::kStabilitySessionEndCompleted, false);
753}
754
755void MetricsService::RecordCompletedSessionEnd() {
[email protected]466f3c12011-03-23 21:20:38756 LogCleanShutdown();
initial.commit09911bf2008-07-26 23:55:29757 RecordBooleanPrefValue(prefs::kStabilitySessionEndCompleted, true);
758}
759
[email protected]410938e02012-10-24 16:33:59760#if defined(OS_ANDROID) || defined(OS_IOS)
[email protected]117fbdf22012-06-26 18:36:39761void MetricsService::OnAppEnterBackground() {
762 scheduler_->Stop();
763
[email protected]84c384e2013-03-01 23:20:19764 MarkAppCleanShutdownAndCommit();
[email protected]117fbdf22012-06-26 18:36:39765
766 // At this point, there's no way of knowing when the process will be
767 // killed, so this has to be treated similar to a shutdown, closing and
768 // persisting all logs. Unlinke a shutdown, the state is primed to be ready
769 // to continue logging and uploading if the process does return.
770 if (recording_active() && state_ >= INITIAL_LOG_READY) {
771 PushPendingLogsToPersistentStorage();
[email protected]410938e02012-10-24 16:33:59772 // Persisting logs closes the current log, so start recording a new log
773 // immediately to capture any background work that might be done before the
774 // process is killed.
775 OpenNewLog();
[email protected]117fbdf22012-06-26 18:36:39776 }
[email protected]117fbdf22012-06-26 18:36:39777}
778
779void MetricsService::OnAppEnterForeground() {
780 PrefService* pref = g_browser_process->local_state();
781 pref->SetBoolean(prefs::kStabilityExitedCleanly, false);
782
783 StartSchedulerIfNecessary();
784}
[email protected]84c384e2013-03-01 23:20:19785#else
786void MetricsService::LogNeedForCleanShutdown() {
787 PrefService* pref = g_browser_process->local_state();
788 pref->SetBoolean(prefs::kStabilityExitedCleanly, false);
789 // Redundant setting to be sure we call for a clean shutdown.
790 clean_shutdown_status_ = NEED_TO_SHUTDOWN;
791}
792#endif // defined(OS_ANDROID) || defined(OS_IOS)
[email protected]117fbdf22012-06-26 18:36:39793
[email protected]7f7f1962011-04-20 15:58:16794void MetricsService::RecordBreakpadRegistration(bool success) {
[email protected]68475e602008-08-22 03:21:15795 if (!success)
[email protected]e73c01972008-08-13 00:18:24796 IncrementPrefValue(prefs::kStabilityBreakpadRegistrationFail);
797 else
798 IncrementPrefValue(prefs::kStabilityBreakpadRegistrationSuccess);
799}
800
801void MetricsService::RecordBreakpadHasDebugger(bool has_debugger) {
802 if (!has_debugger)
803 IncrementPrefValue(prefs::kStabilityDebuggerNotPresent);
804 else
[email protected]68475e602008-08-22 03:21:15805 IncrementPrefValue(prefs::kStabilityDebuggerPresent);
[email protected]e73c01972008-08-13 00:18:24806}
807
initial.commit09911bf2008-07-26 23:55:29808//------------------------------------------------------------------------------
809// private methods
810//------------------------------------------------------------------------------
811
812
813//------------------------------------------------------------------------------
814// Initialization methods
815
816void MetricsService::InitializeMetricsState() {
[email protected]79bf0b72009-04-27 21:30:55817#if defined(OS_POSIX)
[email protected]b4a72d842012-03-22 20:09:09818 network_stats_server_ = chrome_common_net::kEchoTestServerLocation;
819 http_pipelining_test_server_ = chrome_common_net::kPipelineTestServerBaseUrl;
[email protected]79bf0b72009-04-27 21:30:55820#else
821 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
[email protected]d67d1052011-06-09 05:11:41822 network_stats_server_ = dist->GetNetworkStatsServer();
[email protected]adbb3762012-03-09 22:20:08823 http_pipelining_test_server_ = dist->GetHttpPipeliningTestServer();
[email protected]79bf0b72009-04-27 21:30:55824#endif
825
initial.commit09911bf2008-07-26 23:55:29826 PrefService* pref = g_browser_process->local_state();
827 DCHECK(pref);
828
[email protected]225c50842010-01-19 21:19:13829 if ((pref->GetInt64(prefs::kStabilityStatsBuildTime)
830 != MetricsLog::GetBuildTime()) ||
[email protected]ddd231e2010-06-29 20:35:19831 (pref->GetString(prefs::kStabilityStatsVersion)
[email protected]225c50842010-01-19 21:19:13832 != MetricsLog::GetVersionString())) {
[email protected]541f77922009-02-23 21:14:38833 // This is a new version, so we don't want to confuse the stats about the
834 // old version with info that we upload.
835 DiscardOldStabilityStats(pref);
836 pref->SetString(prefs::kStabilityStatsVersion,
[email protected]ddd231e2010-06-29 20:35:19837 MetricsLog::GetVersionString());
[email protected]225c50842010-01-19 21:19:13838 pref->SetInt64(prefs::kStabilityStatsBuildTime,
839 MetricsLog::GetBuildTime());
[email protected]541f77922009-02-23 21:14:38840 }
841
initial.commit09911bf2008-07-26 23:55:29842 // Update session ID
843 session_id_ = pref->GetInteger(prefs::kMetricsSessionID);
844 ++session_id_;
845 pref->SetInteger(prefs::kMetricsSessionID, session_id_);
846
initial.commit09911bf2008-07-26 23:55:29847 // Stability bookkeeping
[email protected]e73c01972008-08-13 00:18:24848 IncrementPrefValue(prefs::kStabilityLaunchCount);
initial.commit09911bf2008-07-26 23:55:29849
[email protected]e73c01972008-08-13 00:18:24850 if (!pref->GetBoolean(prefs::kStabilityExitedCleanly)) {
851 IncrementPrefValue(prefs::kStabilityCrashCount);
[email protected]c0c55e92011-09-10 18:47:30852 // Reset flag, and wait until we call LogNeedForCleanShutdown() before
853 // monitoring.
854 pref->SetBoolean(prefs::kStabilityExitedCleanly, true);
initial.commit09911bf2008-07-26 23:55:29855 }
[email protected]e73c01972008-08-13 00:18:24856
[email protected]e73c01972008-08-13 00:18:24857 if (!pref->GetBoolean(prefs::kStabilitySessionEndCompleted)) {
858 IncrementPrefValue(prefs::kStabilityIncompleteSessionEndCount);
[email protected]c9abf242009-07-18 06:00:38859 // This is marked false when we get a WM_ENDSESSION.
860 pref->SetBoolean(prefs::kStabilitySessionEndCompleted, true);
initial.commit09911bf2008-07-26 23:55:29861 }
initial.commit09911bf2008-07-26 23:55:29862
[email protected]9165f742010-03-10 22:55:01863 // Initialize uptime counters.
864 int64 startup_uptime = MetricsLog::GetIncrementalUptime(pref);
[email protected]ae393ec702010-06-27 16:23:14865 DCHECK_EQ(0, startup_uptime);
[email protected]9165f742010-03-10 22:55:01866 // For backwards compatibility, leave this intact in case Omaha is checking
867 // them. prefs::kStabilityLastTimestampSec may also be useless now.
868 // TODO(jar): Delete these if they have no uses.
[email protected]0bb1a622009-03-04 03:22:32869 pref->SetInt64(prefs::kStabilityLaunchTimeSec, Time::Now().ToTimeT());
870
871 // Bookkeeping for the uninstall metrics.
872 IncrementLongPrefsValue(prefs::kUninstallLaunchCount);
initial.commit09911bf2008-07-26 23:55:29873
874 // Save profile metrics.
875 PrefService* prefs = g_browser_process->local_state();
876 if (prefs) {
877 // Remove the current dictionary and store it for use when sending data to
878 // server. By removing the value we prune potentially dead profiles
879 // (and keys). All valid values are added back once services startup.
880 const DictionaryValue* profile_dictionary =
881 prefs->GetDictionary(prefs::kProfileMetrics);
882 if (profile_dictionary) {
883 // Do a deep copy of profile_dictionary since ClearPref will delete it.
884 profile_dictionary_.reset(static_cast<DictionaryValue*>(
885 profile_dictionary->DeepCopy()));
886 prefs->ClearPref(prefs::kProfileMetrics);
887 }
888 }
889
[email protected]92745242009-06-12 16:52:21890 // Get stats on use of command line.
891 const CommandLine* command_line(CommandLine::ForCurrentProcess());
892 size_t common_commands = 0;
893 if (command_line->HasSwitch(switches::kUserDataDir)) {
894 ++common_commands;
895 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineDatDirCount", 1);
896 }
897
898 if (command_line->HasSwitch(switches::kApp)) {
899 ++common_commands;
900 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineAppModeCount", 1);
901 }
902
[email protected]62b4e522011-07-13 21:46:32903 size_t switch_count = command_line->GetSwitches().size();
904 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineFlagCount", switch_count);
[email protected]92745242009-06-12 16:52:21905 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineUncommonFlagCount",
[email protected]62b4e522011-07-13 21:46:32906 switch_count - common_commands);
[email protected]92745242009-06-12 16:52:21907
initial.commit09911bf2008-07-26 23:55:29908 // Kick off the process of saving the state (so the uptime numbers keep
909 // getting updated) every n minutes.
910 ScheduleNextStateSave();
911}
912
[email protected]c94d7382012-02-28 08:43:40913// static
[email protected]d33e7cc2011-09-23 01:43:56914void MetricsService::InitTaskGetHardwareClass(
[email protected]c94d7382012-02-28 08:43:40915 base::WeakPtr<MetricsService> self,
[email protected]d33e7cc2011-09-23 01:43:56916 base::MessageLoopProxy* target_loop) {
[email protected]d33e7cc2011-09-23 01:43:56917 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
918
919 std::string hardware_class;
920#if defined(OS_CHROMEOS)
921 chromeos::system::StatisticsProvider::GetInstance()->GetMachineStatistic(
922 "hardware_class", &hardware_class);
923#endif // OS_CHROMEOS
924
925 target_loop->PostTask(FROM_HERE,
926 base::Bind(&MetricsService::OnInitTaskGotHardwareClass,
[email protected]c94d7382012-02-28 08:43:40927 self, hardware_class));
[email protected]d33e7cc2011-09-23 01:43:56928}
929
930void MetricsService::OnInitTaskGotHardwareClass(
931 const std::string& hardware_class) {
[email protected]ed0fd002012-04-25 23:10:34932 DCHECK_EQ(INIT_TASK_SCHEDULED, state_);
[email protected]85ed9d42010-06-08 22:37:44933 hardware_class_ = hardware_class;
[email protected]d33e7cc2011-09-23 01:43:56934
[email protected]ebd71962012-12-20 02:56:55935#if defined(ENABLE_PLUGINS)
[email protected]d33e7cc2011-09-23 01:43:56936 // Start the next part of the init task: loading plugin information.
937 PluginService::GetInstance()->GetPlugins(
938 base::Bind(&MetricsService::OnInitTaskGotPluginInfo,
[email protected]c94d7382012-02-28 08:43:40939 self_ptr_factory_.GetWeakPtr()));
[email protected]ebd71962012-12-20 02:56:55940#else
941 std::vector<webkit::WebPluginInfo> plugin_list_empty;
942 OnInitTaskGotPluginInfo(plugin_list_empty);
943#endif // defined(ENABLE_PLUGINS)
[email protected]d33e7cc2011-09-23 01:43:56944}
945
946void MetricsService::OnInitTaskGotPluginInfo(
947 const std::vector<webkit::WebPluginInfo>& plugins) {
[email protected]ed0fd002012-04-25 23:10:34948 DCHECK_EQ(INIT_TASK_SCHEDULED, state_);
[email protected]35fa6a22009-08-15 00:04:01949 plugins_ = plugins;
[email protected]d33e7cc2011-09-23 01:43:56950
[email protected]197c0772012-05-14 23:50:51951 // Schedules a task on a blocking pool thread to gather Google Update
952 // statistics (requires Registry reads).
953 BrowserThread::PostBlockingPoolTask(
954 FROM_HERE,
955 base::Bind(&MetricsService::InitTaskGetGoogleUpdateData,
956 self_ptr_factory_.GetWeakPtr(),
957 MessageLoop::current()->message_loop_proxy()));
958}
959
960// static
961void MetricsService::InitTaskGetGoogleUpdateData(
962 base::WeakPtr<MetricsService> self,
963 base::MessageLoopProxy* target_loop) {
964 GoogleUpdateMetrics google_update_metrics;
965
966#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
967 const bool system_install = GoogleUpdateSettings::IsSystemInstall();
968
969 google_update_metrics.is_system_install = system_install;
970 google_update_metrics.last_started_au =
971 GoogleUpdateSettings::GetGoogleUpdateLastStartedAU(system_install);
972 google_update_metrics.last_checked =
973 GoogleUpdateSettings::GetGoogleUpdateLastChecked(system_install);
974 GoogleUpdateSettings::GetUpdateDetailForGoogleUpdate(
975 system_install,
976 &google_update_metrics.google_update_data);
977 GoogleUpdateSettings::GetUpdateDetail(
978 system_install,
979 &google_update_metrics.product_data);
980#endif // defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
981
982 target_loop->PostTask(FROM_HERE,
983 base::Bind(&MetricsService::OnInitTaskGotGoogleUpdateData,
984 self, google_update_metrics));
985}
986
987void MetricsService::OnInitTaskGotGoogleUpdateData(
988 const GoogleUpdateMetrics& google_update_metrics) {
989 DCHECK_EQ(INIT_TASK_SCHEDULED, state_);
990
991 google_update_metrics_ = google_update_metrics;
992
[email protected]ed0fd002012-04-25 23:10:34993 // Start the next part of the init task: fetching performance data. This will
994 // call into |FinishedReceivingProfilerData()| when the task completes.
995 chrome_browser_metrics::TrackingSynchronizer::FetchProfilerDataAsynchronously(
996 self_ptr_factory_.GetWeakPtr());
997}
998
[email protected]dd98f392013-02-04 13:03:22999void MetricsService::OnUserAction(const std::string& action) {
1000 if (!CanLogNotification())
1001 return;
1002
1003 log_manager_.current_log()->RecordUserAction(action.c_str());
1004 HandleIdleSinceLastTransmission(false);
1005}
1006
[email protected]ed0fd002012-04-25 23:10:341007void MetricsService::ReceivedProfilerData(
1008 const tracked_objects::ProcessDataSnapshot& process_data,
[email protected]f3b357692013-03-22 05:16:131009 int process_type) {
[email protected]ed0fd002012-04-25 23:10:341010 DCHECK_EQ(INIT_TASK_SCHEDULED, state_);
1011
1012 // Upon the first callback, create the initial log so that we can immediately
1013 // save the profiler data.
1014 if (!initial_log_.get())
1015 initial_log_.reset(new MetricsLog(client_id_, session_id_));
1016
1017 initial_log_->RecordProfilerData(process_data, process_type);
1018}
1019
1020void MetricsService::FinishedReceivingProfilerData() {
1021 DCHECK_EQ(INIT_TASK_SCHEDULED, state_);
[email protected]3a7b66d2012-04-26 16:34:161022 state_ = INIT_TASK_DONE;
initial.commit09911bf2008-07-26 23:55:291023}
1024
[email protected]e61003a2012-05-24 17:03:191025int MetricsService::GetLowEntropySource() {
1026 // Note that the default value for the low entropy source and the default pref
[email protected]e88be472e2013-04-26 22:36:361027 // value are both kLowEntropySourceNotSet, which is used to identify if the
1028 // value has been set or not.
1029 if (low_entropy_source_ != kLowEntropySourceNotSet)
[email protected]e61003a2012-05-24 17:03:191030 return low_entropy_source_;
1031
1032 PrefService* pref = g_browser_process->local_state();
1033 const CommandLine* command_line(CommandLine::ForCurrentProcess());
1034 // Only try to load the value from prefs if the user did not request a reset.
1035 // Otherwise, skip to generating a new value.
[email protected]1862c0c2013-04-18 06:30:481036 if (!command_line->HasSwitch(switches::kResetVariationState)) {
[email protected]e63a9ef2012-08-30 15:29:421037 const int value = pref->GetInteger(prefs::kMetricsLowEntropySource);
1038 if (value != kLowEntropySourceNotSet) {
1039 // Ensure the prefs value is in the range [0, kMaxLowEntropySize). Old
1040 // versions of the code would generate values in the range of [1, 8192],
1041 // so the below line ensures 8192 gets mapped to 0 and also guards against
1042 // the case of corrupted values.
1043 low_entropy_source_ = value % kMaxLowEntropySize;
[email protected]0c906e92012-10-18 15:24:131044 UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", false);
[email protected]e61003a2012-05-24 17:03:191045 return low_entropy_source_;
[email protected]e63a9ef2012-08-30 15:29:421046 }
[email protected]e61003a2012-05-24 17:03:191047 }
1048
[email protected]0c906e92012-10-18 15:24:131049 UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", true);
[email protected]e61003a2012-05-24 17:03:191050 low_entropy_source_ = GenerateLowEntropySource();
1051 pref->SetInteger(prefs::kMetricsLowEntropySource, low_entropy_source_);
1052
1053 return low_entropy_source_;
1054}
1055
1056// static
initial.commit09911bf2008-07-26 23:55:291057std::string MetricsService::GenerateClientID() {
[email protected]7e49ad32012-06-14 14:22:071058 return base::GenerateGUID();
initial.commit09911bf2008-07-26 23:55:291059}
1060
initial.commit09911bf2008-07-26 23:55:291061//------------------------------------------------------------------------------
1062// State save methods
1063
1064void MetricsService::ScheduleNextStateSave() {
[email protected]8454aeb2011-11-19 23:38:201065 state_saver_factory_.InvalidateWeakPtrs();
initial.commit09911bf2008-07-26 23:55:291066
1067 MessageLoop::current()->PostDelayedTask(FROM_HERE,
[email protected]8454aeb2011-11-19 23:38:201068 base::Bind(&MetricsService::SaveLocalState,
1069 state_saver_factory_.GetWeakPtr()),
[email protected]fc4252a72012-01-12 21:58:471070 base::TimeDelta::FromMinutes(kSaveStateIntervalMinutes));
initial.commit09911bf2008-07-26 23:55:291071}
1072
1073void MetricsService::SaveLocalState() {
1074 PrefService* pref = g_browser_process->local_state();
1075 if (!pref) {
[email protected]a063c102010-07-22 22:20:191076 NOTREACHED();
initial.commit09911bf2008-07-26 23:55:291077 return;
1078 }
1079
1080 RecordCurrentState(pref);
initial.commit09911bf2008-07-26 23:55:291081
[email protected]fc4252a72012-01-12 21:58:471082 // TODO(jar):110021 Does this run down the batteries????
initial.commit09911bf2008-07-26 23:55:291083 ScheduleNextStateSave();
1084}
1085
1086
1087//------------------------------------------------------------------------------
1088// Recording control methods
1089
[email protected]410938e02012-10-24 16:33:591090void MetricsService::OpenNewLog() {
1091 DCHECK(!log_manager_.current_log());
initial.commit09911bf2008-07-26 23:55:291092
[email protected]29948262012-03-01 12:15:081093 log_manager_.BeginLoggingWithLog(new MetricsLog(client_id_, session_id_),
1094 MetricsLogManager::ONGOING_LOG);
initial.commit09911bf2008-07-26 23:55:291095 if (state_ == INITIALIZED) {
1096 // We only need to schedule that run once.
[email protected]85ed9d42010-06-08 22:37:441097 state_ = INIT_TASK_SCHEDULED;
initial.commit09911bf2008-07-26 23:55:291098
[email protected]85ed9d42010-06-08 22:37:441099 // Schedules a task on the file thread for execution of slower
1100 // initialization steps (such as plugin list generation) necessary
1101 // for sending the initial log. This avoids blocking the main UI
1102 // thread.
[email protected]ed10dd12011-12-07 12:03:421103 BrowserThread::PostDelayedTask(
1104 BrowserThread::FILE,
1105 FROM_HERE,
[email protected]d33e7cc2011-09-23 01:43:561106 base::Bind(&MetricsService::InitTaskGetHardwareClass,
[email protected]c94d7382012-02-28 08:43:401107 self_ptr_factory_.GetWeakPtr(),
[email protected]d33e7cc2011-09-23 01:43:561108 MessageLoop::current()->message_loop_proxy()),
[email protected]7e560102012-03-08 20:58:421109 base::TimeDelta::FromSeconds(kInitializationDelaySeconds));
initial.commit09911bf2008-07-26 23:55:291110 }
1111}
1112
[email protected]410938e02012-10-24 16:33:591113void MetricsService::CloseCurrentLog() {
[email protected]cac267c2011-09-29 15:18:101114 if (!log_manager_.current_log())
initial.commit09911bf2008-07-26 23:55:291115 return;
1116
[email protected]68475e602008-08-22 03:21:151117 // TODO(jar): Integrate bounds on log recording more consistently, so that we
1118 // can stop recording logs that are too big much sooner.
[email protected]cac267c2011-09-29 15:18:101119 if (log_manager_.current_log()->num_events() > kEventLimit) {
[email protected]553dba62009-02-24 19:08:231120 UMA_HISTOGRAM_COUNTS("UMA.Discarded Log Events",
[email protected]cac267c2011-09-29 15:18:101121 log_manager_.current_log()->num_events());
1122 log_manager_.DiscardCurrentLog();
[email protected]410938e02012-10-24 16:33:591123 OpenNewLog(); // Start trivial log to hold our histograms.
[email protected]68475e602008-08-22 03:21:151124 }
1125
[email protected]cac267c2011-09-29 15:18:101126 // Adds to ongoing logs.
1127 log_manager_.current_log()->set_hardware_class(hardware_class_);
[email protected]accdfa62011-09-20 01:56:521128
[email protected]0b33f80b2008-12-17 21:34:361129 // Put incremental data (histogram deltas, and realtime stats deltas) at the
[email protected]147bbc0b2009-01-06 19:37:401130 // end of all log transmissions (initial log handles this separately).
[email protected]024b5cd2011-05-27 03:29:381131 // RecordIncrementalStabilityElements only exists on the derived
1132 // MetricsLog class.
[email protected]279703f2012-01-20 22:23:261133 MetricsLog* current_log =
1134 static_cast<MetricsLog*>(log_manager_.current_log());
[email protected]024b5cd2011-05-27 03:29:381135 DCHECK(current_log);
[email protected]197c0772012-05-14 23:50:511136 current_log->RecordEnvironmentProto(plugins_, google_update_metrics_);
[email protected]fe58acc22012-02-29 01:29:581137 current_log->RecordIncrementalStabilityElements(plugins_);
[email protected]024b5cd2011-05-27 03:29:381138 RecordCurrentHistograms();
initial.commit09911bf2008-07-26 23:55:291139
[email protected]29948262012-03-01 12:15:081140 log_manager_.FinishCurrentLog();
initial.commit09911bf2008-07-26 23:55:291141}
1142
[email protected]cac267c2011-09-29 15:18:101143void MetricsService::PushPendingLogsToPersistentStorage() {
initial.commit09911bf2008-07-26 23:55:291144 if (state_ < INITIAL_LOG_READY)
[email protected]28ab7f92009-01-06 21:39:041145 return; // We didn't and still don't have time to get plugin list etc.
initial.commit09911bf2008-07-26 23:55:291146
[email protected]cac267c2011-09-29 15:18:101147 if (log_manager_.has_staged_log()) {
[email protected]7d41ae6d2012-06-26 08:53:031148 // We may race here, and send second copy of the log later.
[email protected]dc61fe92012-06-12 00:13:501149 MetricsLogManager::StoreType store_type;
[email protected]e3eb0c42013-04-18 06:18:581150 if (current_fetch_.get())
[email protected]dc61fe92012-06-12 00:13:501151 store_type = MetricsLogManager::PROVISIONAL_STORE;
1152 else
1153 store_type = MetricsLogManager::NORMAL_STORE;
[email protected]e7508d82012-05-03 15:59:531154 log_manager_.StoreStagedLogAsUnsent(store_type);
initial.commit09911bf2008-07-26 23:55:291155 }
[email protected]cac267c2011-09-29 15:18:101156 DCHECK(!log_manager_.has_staged_log());
[email protected]410938e02012-10-24 16:33:591157 CloseCurrentLog();
initial.commit09911bf2008-07-26 23:55:291158 StoreUnsentLogs();
[email protected]7d41ae6d2012-06-26 08:53:031159
1160 // If there was a staged and/or current log, then there is now at least one
1161 // log waiting to be uploaded.
1162 if (log_manager_.has_unsent_logs())
1163 state_ = SENDING_OLD_LOGS;
initial.commit09911bf2008-07-26 23:55:291164}
1165
1166//------------------------------------------------------------------------------
1167// Transmission of logs methods
1168
[email protected]7f7f1962011-04-20 15:58:161169void MetricsService::StartSchedulerIfNecessary() {
[email protected]410938e02012-10-24 16:33:591170 // Never schedule cutting or uploading of logs in test mode.
1171 if (test_mode_active_)
1172 return;
1173
1174 // Even if reporting is disabled, the scheduler is needed to trigger the
1175 // creation of the initial log, which must be done in order for any logs to be
1176 // persisted on shutdown or backgrounding.
1177 if (recording_active() && (reporting_active() || state_ < INITIAL_LOG_READY))
[email protected]7f7f1962011-04-20 15:58:161178 scheduler_->Start();
initial.commit09911bf2008-07-26 23:55:291179}
1180
[email protected]7f7f1962011-04-20 15:58:161181void MetricsService::StartScheduledUpload() {
[email protected]cd1ac712012-06-26 08:26:471182 // If we're getting no notifications, then the log won't have much in it, and
1183 // it's possible the computer is about to go to sleep, so don't upload and
1184 // stop the scheduler.
[email protected]410938e02012-10-24 16:33:591185 // If recording has been turned off, the scheduler doesn't need to run.
1186 // If reporting is off, proceed if the initial log hasn't been created, since
1187 // that has to happen in order for logs to be cut and stored when persisting.
[email protected]cd1ac712012-06-26 08:26:471188 // TODO(stuartmorgan): Call Stop() on the schedule when reporting and/or
1189 // recording are turned off instead of letting it fire and then aborting.
1190 if (idle_since_last_transmission_ ||
[email protected]410938e02012-10-24 16:33:591191 !recording_active() ||
1192 (!reporting_active() && state_ >= INITIAL_LOG_READY)) {
[email protected]7f7f1962011-04-20 15:58:161193 scheduler_->Stop();
1194 scheduler_->UploadCancelled();
1195 return;
1196 }
1197
[email protected]c15faf372012-07-11 06:01:341198 // If the callback was to upload an old log, but there no longer is one,
1199 // just report success back to the scheduler to begin the ongoing log
1200 // callbacks.
1201 // TODO(stuartmorgan): Consider removing the distinction between
1202 // SENDING_OLD_LOGS and SENDING_CURRENT_LOGS to simplify the state machine
1203 // now that the log upload flow is the same for both modes.
1204 if (state_ == SENDING_OLD_LOGS && !log_manager_.has_unsent_logs()) {
1205 state_ = SENDING_CURRENT_LOGS;
1206 scheduler_->UploadFinished(true /* healthy */, false /* no unsent logs */);
1207 return;
1208 }
[email protected]cd1ac712012-06-26 08:26:471209 // If there are unsent logs, send the next one. If not, start the asynchronous
1210 // process of finalizing the current log for upload.
1211 if (state_ == SENDING_OLD_LOGS) {
1212 DCHECK(log_manager_.has_unsent_logs());
1213 log_manager_.StageNextLogForUpload();
1214 SendStagedLog();
1215 } else {
1216 StartFinalLogInfoCollection();
1217 }
[email protected]29948262012-03-01 12:15:081218}
1219
1220void MetricsService::StartFinalLogInfoCollection() {
1221 // Begin the multi-step process of collecting memory usage histograms:
1222 // First spawn a task to collect the memory details; when that task is
1223 // finished, it will call OnMemoryDetailCollectionDone. That will in turn
1224 // call HistogramSynchronization to collect histograms from all renderers and
1225 // then call OnHistogramSynchronizationDone to continue processing.
[email protected]d119f222012-06-08 02:33:271226 DCHECK(!waiting_for_asynchronous_reporting_step_);
1227 waiting_for_asynchronous_reporting_step_ = true;
[email protected]7f7f1962011-04-20 15:58:161228
[email protected]2226c222011-11-22 00:08:401229 base::Closure callback =
1230 base::Bind(&MetricsService::OnMemoryDetailCollectionDone,
[email protected]c94d7382012-02-28 08:43:401231 self_ptr_factory_.GetWeakPtr());
[email protected]84c988a2011-04-19 17:56:331232
[email protected]2226c222011-11-22 00:08:401233 scoped_refptr<MetricsMemoryDetails> details(
1234 new MetricsMemoryDetails(callback));
[email protected]4306df72012-04-20 18:58:571235 details->StartFetch(MemoryDetails::UPDATE_USER_METRICS);
[email protected]84c988a2011-04-19 17:56:331236
1237 // Collect WebCore cache information to put into a histogram.
[email protected]f3b1a082011-11-18 00:34:301238 for (content::RenderProcessHost::iterator i(
1239 content::RenderProcessHost::AllHostsIterator());
[email protected]84c988a2011-04-19 17:56:331240 !i.IsAtEnd(); i.Advance())
[email protected]2ccf45c2011-08-19 23:35:501241 i.GetCurrentValue()->Send(new ChromeViewMsg_GetCacheResourceStats());
[email protected]84c988a2011-04-19 17:56:331242}
1243
1244void MetricsService::OnMemoryDetailCollectionDone() {
[email protected]c9a3ef82009-05-28 22:02:461245 DCHECK(IsSingleThreaded());
[email protected]7f7f1962011-04-20 15:58:161246 // This function should only be called as the callback from an ansynchronous
1247 // step.
[email protected]d119f222012-06-08 02:33:271248 DCHECK(waiting_for_asynchronous_reporting_step_);
[email protected]c9a3ef82009-05-28 22:02:461249
[email protected]c9a3ef82009-05-28 22:02:461250 // Create a callback_task for OnHistogramSynchronizationDone.
[email protected]2226c222011-11-22 00:08:401251 base::Closure callback = base::Bind(
1252 &MetricsService::OnHistogramSynchronizationDone,
[email protected]c94d7382012-02-28 08:43:401253 self_ptr_factory_.GetWeakPtr());
[email protected]c9a3ef82009-05-28 22:02:461254
[email protected]908de522011-10-20 00:55:001255 base::StatisticsRecorder::CollectHistogramStats("Browser");
1256
[email protected]c9a3ef82009-05-28 22:02:461257 // Set up the callback to task to call after we receive histograms from all
[email protected]83ab4a282012-07-12 18:19:451258 // child processes. Wait time specifies how long to wait before absolutely
[email protected]c9a3ef82009-05-28 22:02:461259 // calling us back on the task.
[email protected]83ab4a282012-07-12 18:19:451260 content::FetchHistogramsAsynchronously(
[email protected]2226c222011-11-22 00:08:401261 MessageLoop::current(), callback,
[email protected]7e560102012-03-08 20:58:421262 base::TimeDelta::FromMilliseconds(kMaxHistogramGatheringWaitDuration));
[email protected]c9a3ef82009-05-28 22:02:461263}
1264
1265void MetricsService::OnHistogramSynchronizationDone() {
initial.commit09911bf2008-07-26 23:55:291266 DCHECK(IsSingleThreaded());
[email protected]29948262012-03-01 12:15:081267 // This function should only be called as the callback from an ansynchronous
1268 // step.
[email protected]d119f222012-06-08 02:33:271269 DCHECK(waiting_for_asynchronous_reporting_step_);
initial.commit09911bf2008-07-26 23:55:291270
[email protected]d119f222012-06-08 02:33:271271 waiting_for_asynchronous_reporting_step_ = false;
[email protected]29948262012-03-01 12:15:081272 OnFinalLogInfoCollectionDone();
1273}
1274
1275void MetricsService::OnFinalLogInfoCollectionDone() {
[email protected]7f7f1962011-04-20 15:58:161276 // If somehow there is a fetch in progress, we return and hope things work
1277 // out. The scheduler isn't informed since if this happens, the scheduler
1278 // will get a response from the upload.
[email protected]e3eb0c42013-04-18 06:18:581279 DCHECK(!current_fetch_.get());
1280 if (current_fetch_.get())
[email protected]7f7f1962011-04-20 15:58:161281 return;
1282
[email protected]cd1ac712012-06-26 08:26:471283 // Abort if metrics were turned off during the final info gathering.
[email protected]410938e02012-10-24 16:33:591284 if (!recording_active()) {
[email protected]7f7f1962011-04-20 15:58:161285 scheduler_->Stop();
1286 scheduler_->UploadCancelled();
[email protected]d01b8732008-10-16 02:18:071287 return;
1288 }
1289
[email protected]cd1ac712012-06-26 08:26:471290 StageNewLog();
[email protected]410938e02012-10-24 16:33:591291
1292 // If logs shouldn't be uploaded, stop here. It's important that this check
1293 // be after StageNewLog(), otherwise the previous logs will never be loaded,
1294 // and thus the open log won't be persisted.
1295 // TODO(stuartmorgan): This is unnecessarily complicated; restructure loading
1296 // of previous logs to not require running part of the upload logic.
1297 // http://crbug.com/157337
1298 if (!reporting_active()) {
1299 scheduler_->Stop();
1300 scheduler_->UploadCancelled();
1301 return;
1302 }
1303
[email protected]29948262012-03-01 12:15:081304 SendStagedLog();
1305}
1306
[email protected]cd1ac712012-06-26 08:26:471307void MetricsService::StageNewLog() {
[email protected]29948262012-03-01 12:15:081308 if (log_manager_.has_staged_log())
1309 return;
1310
1311 switch (state_) {
1312 case INITIALIZED:
1313 case INIT_TASK_SCHEDULED: // We should be further along by now.
[email protected]dc61fe92012-06-12 00:13:501314 NOTREACHED();
[email protected]29948262012-03-01 12:15:081315 return;
1316
1317 case INIT_TASK_DONE:
[email protected]29948262012-03-01 12:15:081318 PrepareInitialLog();
[email protected]ed0fd002012-04-25 23:10:341319 DCHECK_EQ(INIT_TASK_DONE, state_);
[email protected]29948262012-03-01 12:15:081320 log_manager_.LoadPersistedUnsentLogs();
1321 state_ = INITIAL_LOG_READY;
1322 break;
1323
1324 case SENDING_OLD_LOGS:
[email protected]cd1ac712012-06-26 08:26:471325 NOTREACHED(); // Shouldn't be staging a new log during old log sending.
1326 return;
[email protected]29948262012-03-01 12:15:081327
1328 case SENDING_CURRENT_LOGS:
[email protected]410938e02012-10-24 16:33:591329 CloseCurrentLog();
1330 OpenNewLog();
[email protected]29948262012-03-01 12:15:081331 log_manager_.StageNextLogForUpload();
1332 break;
1333
1334 default:
1335 NOTREACHED();
1336 return;
1337 }
1338
1339 DCHECK(log_manager_.has_staged_log());
1340}
1341
1342void MetricsService::PrepareInitialLog() {
[email protected]ed0fd002012-04-25 23:10:341343 DCHECK_EQ(INIT_TASK_DONE, state_);
[email protected]29948262012-03-01 12:15:081344
[email protected]ed0fd002012-04-25 23:10:341345 DCHECK(initial_log_.get());
1346 initial_log_->set_hardware_class(hardware_class_);
[email protected]197c0772012-05-14 23:50:511347 initial_log_->RecordEnvironment(plugins_, google_update_metrics_,
1348 profile_dictionary_.get());
[email protected]29948262012-03-01 12:15:081349
1350 // Histograms only get written to the current log, so make the new log current
1351 // before writing them.
1352 log_manager_.PauseCurrentLog();
[email protected]ed0fd002012-04-25 23:10:341353 log_manager_.BeginLoggingWithLog(initial_log_.release(),
1354 MetricsLogManager::INITIAL_LOG);
[email protected]29948262012-03-01 12:15:081355 RecordCurrentHistograms();
1356 log_manager_.FinishCurrentLog();
1357 log_manager_.ResumePausedLog();
1358
1359 DCHECK(!log_manager_.has_staged_log());
1360 log_manager_.StageNextLogForUpload();
1361}
1362
1363void MetricsService::StoreUnsentLogs() {
1364 if (state_ < INITIAL_LOG_READY)
1365 return; // We never Recalled the prior unsent logs.
1366
1367 log_manager_.PersistUnsentLogs();
1368}
1369
1370void MetricsService::SendStagedLog() {
1371 DCHECK(log_manager_.has_staged_log());
1372
[email protected]cac267c2011-09-29 15:18:101373 PrepareFetchWithStagedLog();
[email protected]d01b8732008-10-16 02:18:071374
[email protected]e3eb0c42013-04-18 06:18:581375 bool upload_created = (current_fetch_.get() != NULL);
[email protected]d6bebb92012-06-13 23:14:551376 UMA_HISTOGRAM_BOOLEAN("UMA.UploadCreation", upload_created);
1377 if (!upload_created) {
[email protected]d01b8732008-10-16 02:18:071378 // Compression failed, and log discarded :-/.
[email protected]dc61fe92012-06-12 00:13:501379 // Skip this upload and hope things work out next time.
[email protected]cac267c2011-09-29 15:18:101380 log_manager_.DiscardStagedLog();
[email protected]7f7f1962011-04-20 15:58:161381 scheduler_->UploadCancelled();
[email protected]d01b8732008-10-16 02:18:071382 return;
1383 }
1384
[email protected]d119f222012-06-08 02:33:271385 DCHECK(!waiting_for_asynchronous_reporting_step_);
[email protected]d119f222012-06-08 02:33:271386 waiting_for_asynchronous_reporting_step_ = true;
[email protected]dc61fe92012-06-12 00:13:501387
[email protected]e3eb0c42013-04-18 06:18:581388 current_fetch_->Start();
[email protected]d01b8732008-10-16 02:18:071389
1390 HandleIdleSinceLastTransmission(true);
1391}
1392
[email protected]cac267c2011-09-29 15:18:101393void MetricsService::PrepareFetchWithStagedLog() {
[email protected]dc61fe92012-06-12 00:13:501394 DCHECK(log_manager_.has_staged_log());
[email protected]cac78842008-11-27 01:02:201395
[email protected]fe58acc22012-02-29 01:29:581396 // Prepare the protobuf version.
[email protected]e3eb0c42013-04-18 06:18:581397 DCHECK(!current_fetch_.get());
[email protected]fe58acc22012-02-29 01:29:581398 if (log_manager_.has_staged_log_proto()) {
[email protected]e3eb0c42013-04-18 06:18:581399 current_fetch_.reset(net::URLFetcher::Create(
1400 GURL(kServerUrlProto), net::URLFetcher::POST, this));
1401 current_fetch_->SetRequestContext(
[email protected]fe58acc22012-02-29 01:29:581402 g_browser_process->system_request_context());
[email protected]e3eb0c42013-04-18 06:18:581403 current_fetch_->SetUploadData(kMimeTypeProto,
1404 log_manager_.staged_log_text().proto);
[email protected]fe58acc22012-02-29 01:29:581405 // We already drop cookies server-side, but we might as well strip them out
1406 // client-side as well.
[email protected]e3eb0c42013-04-18 06:18:581407 current_fetch_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
1408 net::LOAD_DO_NOT_SEND_COOKIES);
[email protected]fe58acc22012-02-29 01:29:581409 }
initial.commit09911bf2008-07-26 23:55:291410}
1411
[email protected]10c2d692012-05-11 05:32:231412void MetricsService::OnURLFetchComplete(const net::URLFetcher* source) {
[email protected]d119f222012-06-08 02:33:271413 DCHECK(waiting_for_asynchronous_reporting_step_);
[email protected]fe58acc22012-02-29 01:29:581414
1415 // We're not allowed to re-use the existing |URLFetcher|s, so free them here.
[email protected]e3eb0c42013-04-18 06:18:581416 // Note however that |source| is aliased to the fetcher, so we should be
[email protected]4266def22012-05-17 01:02:401417 // careful not to delete it too early.
[email protected]e3eb0c42013-04-18 06:18:581418 DCHECK_EQ(current_fetch_.get(), source);
1419 scoped_ptr<net::URLFetcher> s(current_fetch_.Pass());
[email protected]fe58acc22012-02-29 01:29:581420
[email protected]dc61fe92012-06-12 00:13:501421 int response_code = source->GetResponseCode();
[email protected]fe58acc22012-02-29 01:29:581422
[email protected]dc61fe92012-06-12 00:13:501423 // Log a histogram to track response success vs. failure rates.
[email protected]e3eb0c42013-04-18 06:18:581424 UMA_HISTOGRAM_ENUMERATION("UMA.UploadResponseStatus.Protobuf",
1425 ResponseCodeToStatus(response_code),
1426 NUM_RESPONSE_STATUSES);
[email protected]fe58acc22012-02-29 01:29:581427
[email protected]e7508d82012-05-03 15:59:531428 // If the upload was provisionally stored, drop it now that the upload is
1429 // known to have gone through.
1430 log_manager_.DiscardLastProvisionalStore();
initial.commit09911bf2008-07-26 23:55:291431
[email protected]dc61fe92012-06-12 00:13:501432 bool upload_succeeded = response_code == 200;
[email protected]7f7f1962011-04-20 15:58:161433
[email protected]0eb34fee2009-01-21 08:04:381434 // Provide boolean for error recovery (allow us to ignore response_code).
[email protected]dc6f4962009-02-13 01:25:501435 bool discard_log = false;
[email protected]e3eb0c42013-04-18 06:18:581436 const size_t log_size = log_manager_.staged_log_text().proto.length();
[email protected]dc61fe92012-06-12 00:13:501437 if (!upload_succeeded && log_size > kUploadLogAvoidRetransmitSize) {
1438 UMA_HISTOGRAM_COUNTS("UMA.Large Rejected Log was Discarded",
1439 static_cast<int>(log_size));
[email protected]0eb34fee2009-01-21 08:04:381440 discard_log = true;
[email protected]dc61fe92012-06-12 00:13:501441 } else if (response_code == 400) {
[email protected]0eb34fee2009-01-21 08:04:381442 // Bad syntax. Retransmission won't work.
[email protected]0eb34fee2009-01-21 08:04:381443 discard_log = true;
[email protected]68475e602008-08-22 03:21:151444 }
1445
[email protected]e3eb0c42013-04-18 06:18:581446 if (upload_succeeded || discard_log)
1447 log_manager_.DiscardStagedLogProto();
[email protected]dc61fe92012-06-12 00:13:501448
1449 waiting_for_asynchronous_reporting_step_ = false;
1450
1451 if (!log_manager_.has_staged_log()) {
initial.commit09911bf2008-07-26 23:55:291452 switch (state_) {
1453 case INITIAL_LOG_READY:
[email protected]cd1ac712012-06-26 08:26:471454 state_ = log_manager_.has_unsent_logs() ? SENDING_OLD_LOGS
1455 : SENDING_CURRENT_LOGS;
initial.commit09911bf2008-07-26 23:55:291456 break;
1457
initial.commit09911bf2008-07-26 23:55:291458 case SENDING_OLD_LOGS:
[email protected]d53e2232011-06-30 15:54:571459 // Store the updated list to disk now that the removed log is uploaded.
initial.commit09911bf2008-07-26 23:55:291460 StoreUnsentLogs();
[email protected]cd1ac712012-06-26 08:26:471461 if (!log_manager_.has_unsent_logs())
1462 state_ = SENDING_CURRENT_LOGS;
initial.commit09911bf2008-07-26 23:55:291463 break;
1464
1465 case SENDING_CURRENT_LOGS:
1466 break;
1467
1468 default:
[email protected]a063c102010-07-22 22:20:191469 NOTREACHED();
initial.commit09911bf2008-07-26 23:55:291470 break;
1471 }
[email protected]d01b8732008-10-16 02:18:071472
[email protected]cac267c2011-09-29 15:18:101473 if (log_manager_.has_unsent_logs())
[email protected]ed0fd002012-04-25 23:10:341474 DCHECK_LT(state_, SENDING_CURRENT_LOGS);
initial.commit09911bf2008-07-26 23:55:291475 }
[email protected]252873ef2008-08-04 21:59:451476
[email protected]7f7f1962011-04-20 15:58:161477 // Error 400 indicates a problem with the log, not with the server, so
1478 // don't consider that a sign that the server is in trouble.
[email protected]dc61fe92012-06-12 00:13:501479 bool server_is_healthy = upload_succeeded || response_code == 400;
[email protected]dc61fe92012-06-12 00:13:501480 scheduler_->UploadFinished(server_is_healthy, log_manager_.has_unsent_logs());
[email protected]d67d1052011-06-09 05:11:411481
1482 // Collect network stats if UMA upload succeeded.
[email protected]b8ddb052012-04-19 02:36:061483 IOThread* io_thread = g_browser_process->io_thread();
1484 if (server_is_healthy && io_thread) {
1485 chrome_browser_net::CollectNetworkStats(network_stats_server_, io_thread);
[email protected]adbb3762012-03-09 22:20:081486 chrome_browser_net::CollectPipeliningCapabilityStatsOnUIThread(
[email protected]b8ddb052012-04-19 02:36:061487 http_pipelining_test_server_, io_thread);
[email protected]adbb3762012-03-09 22:20:081488 }
initial.commit09911bf2008-07-26 23:55:291489}
1490
[email protected]459f3502012-09-17 17:08:121491void MetricsService::LogWindowOrTabChange(int type, uintptr_t window_or_tab) {
[email protected]534e54b2008-08-13 15:40:091492 int controller_id = -1;
initial.commit09911bf2008-07-26 23:55:291493 MetricsLog::WindowEventType window_type;
1494
1495 // Note: since we stop all logging when a single OTR session is active, it is
1496 // possible that we start getting notifications about a window that we don't
1497 // know about.
[email protected]534e54b2008-08-13 15:40:091498 if (window_map_.find(window_or_tab) == window_map_.end()) {
1499 controller_id = next_window_id_++;
1500 window_map_[window_or_tab] = controller_id;
initial.commit09911bf2008-07-26 23:55:291501 } else {
[email protected]534e54b2008-08-13 15:40:091502 controller_id = window_map_[window_or_tab];
initial.commit09911bf2008-07-26 23:55:291503 }
[email protected]92745242009-06-12 16:52:211504 DCHECK_NE(controller_id, -1);
initial.commit09911bf2008-07-26 23:55:291505
[email protected]432115822011-07-10 15:52:271506 switch (type) {
[email protected]884033e2012-04-16 19:38:421507 case chrome::NOTIFICATION_TAB_PARENTED:
[email protected]432115822011-07-10 15:52:271508 case chrome::NOTIFICATION_BROWSER_OPENED:
initial.commit09911bf2008-07-26 23:55:291509 window_type = MetricsLog::WINDOW_CREATE;
1510 break;
1511
[email protected]884033e2012-04-16 19:38:421512 case chrome::NOTIFICATION_TAB_CLOSING:
[email protected]432115822011-07-10 15:52:271513 case chrome::NOTIFICATION_BROWSER_CLOSED:
[email protected]534e54b2008-08-13 15:40:091514 window_map_.erase(window_map_.find(window_or_tab));
initial.commit09911bf2008-07-26 23:55:291515 window_type = MetricsLog::WINDOW_DESTROY;
1516 break;
1517
1518 default:
[email protected]a063c102010-07-22 22:20:191519 NOTREACHED();
[email protected]68d74f02009-02-13 01:36:501520 return;
initial.commit09911bf2008-07-26 23:55:291521 }
1522
[email protected]534e54b2008-08-13 15:40:091523 // TODO(brettw) we should have some kind of ID for the parent.
[email protected]cac267c2011-09-29 15:18:101524 log_manager_.current_log()->RecordWindowEvent(window_type, controller_id, 0);
initial.commit09911bf2008-07-26 23:55:291525}
1526
[email protected]6c2381d2011-10-19 02:52:531527void MetricsService::LogLoadComplete(
1528 int type,
1529 const content::NotificationSource& source,
1530 const content::NotificationDetails& details) {
[email protected]ad50def52011-10-19 23:17:071531 if (details == content::NotificationService::NoDetails())
initial.commit09911bf2008-07-26 23:55:291532 return;
1533
[email protected]68475e602008-08-22 03:21:151534 // TODO(jar): There is a bug causing this to be called too many times, and
1535 // the log overflows. For now, we won't record these events.
[email protected]553dba62009-02-24 19:08:231536 UMA_HISTOGRAM_COUNTS("UMA.LogLoadComplete called", 1);
[email protected]68475e602008-08-22 03:21:151537 return;
1538
[email protected]6c2381d2011-10-19 02:52:531539 const content::Details<LoadNotificationDetails> load_details(details);
[email protected]534e54b2008-08-13 15:40:091540 int controller_id = window_map_[details.map_key()];
[email protected]cac267c2011-09-29 15:18:101541 log_manager_.current_log()->RecordLoadEvent(controller_id,
[email protected]09d31d52012-03-11 22:30:271542 load_details->url,
1543 load_details->origin,
1544 load_details->session_index,
1545 load_details->load_time);
initial.commit09911bf2008-07-26 23:55:291546}
1547
[email protected]57ecc4b2010-08-11 03:02:511548void MetricsService::IncrementPrefValue(const char* path) {
[email protected]e73c01972008-08-13 00:18:241549 PrefService* pref = g_browser_process->local_state();
1550 DCHECK(pref);
1551 int value = pref->GetInteger(path);
1552 pref->SetInteger(path, value + 1);
1553}
1554
[email protected]57ecc4b2010-08-11 03:02:511555void MetricsService::IncrementLongPrefsValue(const char* path) {
[email protected]0bb1a622009-03-04 03:22:321556 PrefService* pref = g_browser_process->local_state();
1557 DCHECK(pref);
1558 int64 value = pref->GetInt64(path);
[email protected]b42c5e42010-06-03 20:43:251559 pref->SetInt64(path, value + 1);
[email protected]0bb1a622009-03-04 03:22:321560}
1561
initial.commit09911bf2008-07-26 23:55:291562void MetricsService::LogLoadStarted() {
[email protected]5d490e42012-08-30 05:16:431563 content::RecordAction(content::UserMetricsAction("PageLoad"));
[email protected]dd8d12a2011-09-02 02:10:151564 HISTOGRAM_ENUMERATION("Chrome.UmaPageloadCounter", 1, 2);
[email protected]e73c01972008-08-13 00:18:241565 IncrementPrefValue(prefs::kStabilityPageLoadCount);
[email protected]0bb1a622009-03-04 03:22:321566 IncrementLongPrefsValue(prefs::kUninstallMetricsPageLoadCount);
[email protected]0b33f80b2008-12-17 21:34:361567 // We need to save the prefs, as page load count is a critical stat, and it
1568 // might be lost due to a crash :-(.
initial.commit09911bf2008-07-26 23:55:291569}
1570
[email protected]c3721482012-03-23 16:21:481571void MetricsService::LogRendererCrash(content::RenderProcessHost* host,
1572 base::TerminationStatus status,
[email protected]f1675202012-07-09 15:18:001573 int exit_code) {
[email protected]f3b1a082011-11-18 00:34:301574 Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
[email protected]6f371442011-11-09 06:45:461575 ExtensionService* service = profile->GetExtensionService();
1576 bool was_extension_process =
[email protected]f3b1a082011-11-18 00:34:301577 service && service->process_map()->Contains(host->GetID());
[email protected]c3721482012-03-23 16:21:481578 if (status == base::TERMINATION_STATUS_PROCESS_CRASHED ||
1579 status == base::TERMINATION_STATUS_ABNORMAL_TERMINATION) {
[email protected]d7c1fa62012-06-15 23:35:301580 if (was_extension_process) {
[email protected]718eab62011-10-05 21:16:521581 IncrementPrefValue(prefs::kStabilityExtensionRendererCrashCount);
[email protected]d7c1fa62012-06-15 23:35:301582
[email protected]1026afd2013-03-20 14:28:541583 UMA_HISTOGRAM_SPARSE_SLOWLY("CrashExitCodes.Extension",
1584 MapCrashExitCodeForHistogram(exit_code));
[email protected]d7c1fa62012-06-15 23:35:301585 } else {
[email protected]718eab62011-10-05 21:16:521586 IncrementPrefValue(prefs::kStabilityRendererCrashCount);
initial.commit09911bf2008-07-26 23:55:291587
[email protected]1026afd2013-03-20 14:28:541588 UMA_HISTOGRAM_SPARSE_SLOWLY("CrashExitCodes.Renderer",
1589 MapCrashExitCodeForHistogram(exit_code));
[email protected]d7c1fa62012-06-15 23:35:301590 }
1591
[email protected]718eab62011-10-05 21:16:521592 UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.ChildCrashes",
1593 was_extension_process ? 2 : 1);
[email protected]c3721482012-03-23 16:21:481594 } else if (status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED) {
[email protected]718eab62011-10-05 21:16:521595 UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.ChildKills",
1596 was_extension_process ? 2 : 1);
[email protected]f1675202012-07-09 15:18:001597 } else if (status == base::TERMINATION_STATUS_STILL_RUNNING) {
1598 UMA_HISTOGRAM_PERCENTAGE("BrowserRenderProcessHost.DisconnectedAlive",
[email protected]718eab62011-10-05 21:16:521599 was_extension_process ? 2 : 1);
1600 }
1601 }
[email protected]1f085622009-12-04 05:33:451602
initial.commit09911bf2008-07-26 23:55:291603void MetricsService::LogRendererHang() {
[email protected]e73c01972008-08-13 00:18:241604 IncrementPrefValue(prefs::kStabilityRendererHangCount);
initial.commit09911bf2008-07-26 23:55:291605}
1606
[email protected]c0c55e92011-09-10 18:47:301607bool MetricsService::UmaMetricsProperlyShutdown() {
1608 CHECK(clean_shutdown_status_ == CLEANLY_SHUTDOWN ||
1609 clean_shutdown_status_ == NEED_TO_SHUTDOWN);
1610 return clean_shutdown_status_ == CLEANLY_SHUTDOWN;
1611}
1612
[email protected]466f3c12011-03-23 21:20:381613void MetricsService::LogCleanShutdown() {
[email protected]acd55b32011-09-05 17:35:311614 // Redundant hack to write pref ASAP.
[email protected]84c384e2013-03-01 23:20:191615 MarkAppCleanShutdownAndCommit();
1616
[email protected]c0c55e92011-09-10 18:47:301617 // Redundant setting to assure that we always reset this value at shutdown
1618 // (and that we don't use some alternate path, and not call LogCleanShutdown).
1619 clean_shutdown_status_ = CLEANLY_SHUTDOWN;
[email protected]acd55b32011-09-05 17:35:311620
[email protected]466f3c12011-03-23 21:20:381621 RecordBooleanPrefValue(prefs::kStabilityExitedCleanly, true);
1622}
1623
[email protected]c1834a92011-01-21 18:21:031624#if defined(OS_CHROMEOS)
1625void MetricsService::LogChromeOSCrash(const std::string &crash_type) {
1626 if (crash_type == "user")
1627 IncrementPrefValue(prefs::kStabilityOtherUserCrashCount);
1628 else if (crash_type == "kernel")
1629 IncrementPrefValue(prefs::kStabilityKernelCrashCount);
1630 else if (crash_type == "uncleanshutdown")
1631 IncrementPrefValue(prefs::kStabilitySystemUncleanShutdownCount);
1632 else
1633 NOTREACHED() << "Unexpected Chrome OS crash type " << crash_type;
1634 // Wake up metrics logs sending if necessary now that new
1635 // log data is available.
1636 HandleIdleSinceLastTransmission(false);
1637}
1638#endif // OS_CHROMEOS
1639
[email protected]650b2d52013-02-10 03:41:451640void MetricsService::LogPluginLoadingError(const base::FilePath& plugin_path) {
[email protected]cd937072012-07-02 09:00:291641 webkit::WebPluginInfo plugin;
1642 bool success =
1643 content::PluginService::GetInstance()->GetPluginInfoByPath(plugin_path,
1644 &plugin);
1645 DCHECK(success);
1646 ChildProcessStats& stats = child_process_stats_buffer_[plugin.name];
1647 // Initialize the type if this entry is new.
1648 if (stats.process_type == content::PROCESS_TYPE_UNKNOWN) {
1649 // The plug-in process might not actually of type PLUGIN (which means
1650 // NPAPI), but we only care that it is *a* plug-in process.
1651 stats.process_type = content::PROCESS_TYPE_PLUGIN;
1652 } else {
1653 DCHECK(IsPluginProcess(stats.process_type));
1654 }
1655 stats.loading_errors++;
1656}
1657
[email protected]f4eaf7b92013-02-28 22:00:401658MetricsService::ChildProcessStats& MetricsService::GetChildProcessStats(
1659 const content::ChildProcessData& data) {
1660 const string16& child_name = data.name;
[email protected]f3b357692013-03-22 05:16:131661 if (!ContainsKey(child_process_stats_buffer_, child_name)) {
1662 child_process_stats_buffer_[child_name] =
1663 ChildProcessStats(data.process_type);
1664 }
[email protected]f4eaf7b92013-02-28 22:00:401665 return child_process_stats_buffer_[child_name];
initial.commit09911bf2008-07-26 23:55:291666}
1667
[email protected]604d8a062012-03-16 21:37:461668void MetricsService::LogKeywordCount(size_t keyword_count) {
initial.commit09911bf2008-07-26 23:55:291669 PrefService* pref = g_browser_process->local_state();
1670 DCHECK(pref);
[email protected]604d8a062012-03-16 21:37:461671 pref->SetInteger(prefs::kNumKeywords, static_cast<int>(keyword_count));
initial.commit09911bf2008-07-26 23:55:291672 ScheduleNextStateSave();
1673}
1674
1675void MetricsService::RecordPluginChanges(PrefService* pref) {
[email protected]f8628c22011-04-05 12:10:181676 ListPrefUpdate update(pref, prefs::kStabilityPluginStats);
1677 ListValue* plugins = update.Get();
initial.commit09911bf2008-07-26 23:55:291678 DCHECK(plugins);
1679
1680 for (ListValue::iterator value_iter = plugins->begin();
1681 value_iter != plugins->end(); ++value_iter) {
1682 if (!(*value_iter)->IsType(Value::TYPE_DICTIONARY)) {
[email protected]a063c102010-07-22 22:20:191683 NOTREACHED();
initial.commit09911bf2008-07-26 23:55:291684 continue;
1685 }
1686
1687 DictionaryValue* plugin_dict = static_cast<DictionaryValue*>(*value_iter);
[email protected]57ecc4b2010-08-11 03:02:511688 std::string plugin_name;
[email protected]8e50b602009-03-03 22:59:431689 plugin_dict->GetString(prefs::kStabilityPluginName, &plugin_name);
[email protected]6470ee8f2009-03-03 20:46:401690 if (plugin_name.empty()) {
[email protected]a063c102010-07-22 22:20:191691 NOTREACHED();
initial.commit09911bf2008-07-26 23:55:291692 continue;
1693 }
1694
[email protected]57ecc4b2010-08-11 03:02:511695 // TODO(viettrungluu): remove conversions
[email protected]68b9e72b2011-08-05 23:08:221696 string16 name16 = UTF8ToUTF16(plugin_name);
1697 if (child_process_stats_buffer_.find(name16) ==
1698 child_process_stats_buffer_.end()) {
initial.commit09911bf2008-07-26 23:55:291699 continue;
[email protected]68b9e72b2011-08-05 23:08:221700 }
initial.commit09911bf2008-07-26 23:55:291701
[email protected]68b9e72b2011-08-05 23:08:221702 ChildProcessStats stats = child_process_stats_buffer_[name16];
initial.commit09911bf2008-07-26 23:55:291703 if (stats.process_launches) {
1704 int launches = 0;
[email protected]8e50b602009-03-03 22:59:431705 plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches);
initial.commit09911bf2008-07-26 23:55:291706 launches += stats.process_launches;
[email protected]8e50b602009-03-03 22:59:431707 plugin_dict->SetInteger(prefs::kStabilityPluginLaunches, launches);
initial.commit09911bf2008-07-26 23:55:291708 }
1709 if (stats.process_crashes) {
1710 int crashes = 0;
[email protected]8e50b602009-03-03 22:59:431711 plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes);
initial.commit09911bf2008-07-26 23:55:291712 crashes += stats.process_crashes;
[email protected]8e50b602009-03-03 22:59:431713 plugin_dict->SetInteger(prefs::kStabilityPluginCrashes, crashes);
initial.commit09911bf2008-07-26 23:55:291714 }
1715 if (stats.instances) {
1716 int instances = 0;
[email protected]8e50b602009-03-03 22:59:431717 plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances);
initial.commit09911bf2008-07-26 23:55:291718 instances += stats.instances;
[email protected]8e50b602009-03-03 22:59:431719 plugin_dict->SetInteger(prefs::kStabilityPluginInstances, instances);
initial.commit09911bf2008-07-26 23:55:291720 }
[email protected]cd937072012-07-02 09:00:291721 if (stats.loading_errors) {
1722 int loading_errors = 0;
1723 plugin_dict->GetInteger(prefs::kStabilityPluginLoadingErrors,
1724 &loading_errors);
1725 loading_errors += stats.loading_errors;
1726 plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors,
1727 loading_errors);
1728 }
initial.commit09911bf2008-07-26 23:55:291729
[email protected]68b9e72b2011-08-05 23:08:221730 child_process_stats_buffer_.erase(name16);
initial.commit09911bf2008-07-26 23:55:291731 }
1732
1733 // Now go through and add dictionaries for plugins that didn't already have
1734 // reports in Local State.
[email protected]68b9e72b2011-08-05 23:08:221735 for (std::map<string16, ChildProcessStats>::iterator cache_iter =
[email protected]a27a9382009-02-11 23:55:101736 child_process_stats_buffer_.begin();
1737 cache_iter != child_process_stats_buffer_.end(); ++cache_iter) {
[email protected]a27a9382009-02-11 23:55:101738 ChildProcessStats stats = cache_iter->second;
[email protected]0d84c5d2009-10-09 01:10:421739
1740 // Insert only plugins information into the plugins list.
[email protected]8d5f1dae2011-11-11 14:30:411741 if (!IsPluginProcess(stats.process_type))
[email protected]0d84c5d2009-10-09 01:10:421742 continue;
1743
[email protected]57ecc4b2010-08-11 03:02:511744 // TODO(viettrungluu): remove conversion
[email protected]68b9e72b2011-08-05 23:08:221745 std::string plugin_name = UTF16ToUTF8(cache_iter->first);
[email protected]0d84c5d2009-10-09 01:10:421746
initial.commit09911bf2008-07-26 23:55:291747 DictionaryValue* plugin_dict = new DictionaryValue;
1748
[email protected]8e50b602009-03-03 22:59:431749 plugin_dict->SetString(prefs::kStabilityPluginName, plugin_name);
1750 plugin_dict->SetInteger(prefs::kStabilityPluginLaunches,
initial.commit09911bf2008-07-26 23:55:291751 stats.process_launches);
[email protected]8e50b602009-03-03 22:59:431752 plugin_dict->SetInteger(prefs::kStabilityPluginCrashes,
initial.commit09911bf2008-07-26 23:55:291753 stats.process_crashes);
[email protected]8e50b602009-03-03 22:59:431754 plugin_dict->SetInteger(prefs::kStabilityPluginInstances,
initial.commit09911bf2008-07-26 23:55:291755 stats.instances);
[email protected]cd937072012-07-02 09:00:291756 plugin_dict->SetInteger(prefs::kStabilityPluginLoadingErrors,
1757 stats.loading_errors);
initial.commit09911bf2008-07-26 23:55:291758 plugins->Append(plugin_dict);
1759 }
[email protected]a27a9382009-02-11 23:55:101760 child_process_stats_buffer_.clear();
initial.commit09911bf2008-07-26 23:55:291761}
1762
[email protected]dd98f392013-02-04 13:03:221763bool MetricsService::CanLogNotification() {
[email protected]2c910b72011-03-08 21:16:321764 // We simply don't log anything to UMA if there is a single incognito
initial.commit09911bf2008-07-26 23:55:291765 // session visible. The problem is that we always notify using the orginal
1766 // profile in order to simplify notification processing.
[email protected]e764e582012-08-01 03:01:291767 return !chrome::IsOffTheRecordSessionActive();
initial.commit09911bf2008-07-26 23:55:291768}
1769
[email protected]57ecc4b2010-08-11 03:02:511770void MetricsService::RecordBooleanPrefValue(const char* path, bool value) {
initial.commit09911bf2008-07-26 23:55:291771 DCHECK(IsSingleThreaded());
1772
1773 PrefService* pref = g_browser_process->local_state();
1774 DCHECK(pref);
1775
1776 pref->SetBoolean(path, value);
1777 RecordCurrentState(pref);
1778}
1779
1780void MetricsService::RecordCurrentState(PrefService* pref) {
[email protected]0bb1a622009-03-04 03:22:321781 pref->SetInt64(prefs::kStabilityLastTimestampSec, Time::Now().ToTimeT());
initial.commit09911bf2008-07-26 23:55:291782
1783 RecordPluginChanges(pref);
1784}
1785
[email protected]8d5f1dae2011-11-11 14:30:411786// static
[email protected]f3b357692013-03-22 05:16:131787bool MetricsService::IsPluginProcess(int process_type) {
1788 return (process_type == content::PROCESS_TYPE_PLUGIN ||
1789 process_type == content::PROCESS_TYPE_PPAPI_PLUGIN ||
1790 process_type == content::PROCESS_TYPE_PPAPI_BROKER);
[email protected]8d5f1dae2011-11-11 14:30:411791}
1792
[email protected]5ccaa412009-11-13 22:00:161793#if defined(OS_CHROMEOS)
[email protected]29cf16772010-04-21 15:13:471794void MetricsService::StartExternalMetrics() {
[email protected]5ccaa412009-11-13 22:00:161795 external_metrics_ = new chromeos::ExternalMetrics;
[email protected]29cf16772010-04-21 15:13:471796 external_metrics_->Start();
[email protected]5ccaa412009-11-13 22:00:161797}
1798#endif
[email protected]3819f2ee2011-08-21 09:44:381799
1800// static
1801bool MetricsServiceHelper::IsMetricsReportingEnabled() {
1802 bool result = false;
1803 const PrefService* local_state = g_browser_process->local_state();
1804 if (local_state) {
1805 const PrefService::Preference* uma_pref =
1806 local_state->FindPreference(prefs::kMetricsReportingEnabled);
1807 if (uma_pref) {
1808 bool success = uma_pref->GetValue()->GetAsBoolean(&result);
1809 DCHECK(success);
1810 }
1811 }
1812 return result;
1813}