blob: bc4e094192ec1b935aefd992000489888285b329 [file] [log] [blame]
[email protected]f90bf0d92011-01-13 02:12:441// Copyright (c) 2011 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//
10// A MetricsService instance is typically created at application startup. It
11// is the central controller for the acquisition of log data, and the automatic
12// transmission of that log data to an external server. Its major job is to
13// manage logs, grouping them for transmission, and transmitting them. As part
14// of its grouping, MS finalizes logs by including some just-in-time gathered
15// memory statistics, snapshotting the current stats of numerous histograms,
16// closing the logs, translating to XML text, and compressing the results for
17// transmission. Transmission includes submitting a compressed log as data in a
[email protected]281d2882009-01-20 20:32:4218// URL-post, and retransmitting (or retaining at process termination) if the
initial.commit09911bf2008-07-26 23:55:2919// attempted transmission failed. Retention across process terminations is done
[email protected]46f89e142010-07-19 08:00:4220// using the the PrefServices facilities. The retained logs (the ones that never
21// got transmitted) are compressed and base64-encoded before being persisted.
initial.commit09911bf2008-07-26 23:55:2922//
[email protected]281d2882009-01-20 20:32:4223// Logs fall into one of two categories: "initial logs," and "ongoing logs."
24// There is at most one initial log sent for each complete run of the chromium
initial.commit09911bf2008-07-26 23:55:2925// product (from startup, to browser shutdown). An initial log is generally
26// transmitted some short time (1 minute?) after startup, and includes stats
27// such as recent crash info, the number and types of plugins, etc. The
[email protected]281d2882009-01-20 20:32:4228// external server's response to the initial log conceptually tells this MS if
29// it should continue transmitting logs (during this session). The server
30// response can actually be much more detailed, and always includes (at a
31// minimum) how often additional ongoing logs should be sent.
initial.commit09911bf2008-07-26 23:55:2932//
33// After the above initial log, a series of ongoing logs will be transmitted.
34// The first ongoing log actually begins to accumulate information stating when
35// the MS was first constructed. Note that even though the initial log is
36// commonly sent a full minute after startup, the initial log does not include
37// much in the way of user stats. The most common interlog period (delay)
[email protected]0b33f80b2008-12-17 21:34:3638// is 20 minutes. That time period starts when the first user action causes a
initial.commit09911bf2008-07-26 23:55:2939// logging event. This means that if there is no user action, there may be long
[email protected]281d2882009-01-20 20:32:4240// periods without any (ongoing) log transmissions. Ongoing logs typically
initial.commit09911bf2008-07-26 23:55:2941// contain very detailed records of user activities (ex: opened tab, closed
42// tab, fetched URL, maximized window, etc.) In addition, just before an
43// ongoing log is closed out, a call is made to gather memory statistics. Those
44// memory statistics are deposited into a histogram, and the log finalization
45// code is then called. In the finalization, a call to a Histogram server
46// acquires a list of all local histograms that have been flagged for upload
[email protected]281d2882009-01-20 20:32:4247// to the UMA server. The finalization also acquires a the most recent number
48// of page loads, along with any counts of renderer or plugin crashes.
initial.commit09911bf2008-07-26 23:55:2949//
50// When the browser shuts down, there will typically be a fragment of an ongoing
51// log that has not yet been transmitted. At shutdown time, that fragment
52// is closed (including snapshotting histograms), and converted to text. Note
53// that memory stats are not gathered during shutdown, as gathering *might* be
54// too time consuming. The textual representation of the fragment of the
55// ongoing log is then stored persistently as a string in the PrefServices, for
56// potential transmission during a future run of the product.
57//
58// There are two slightly abnormal shutdown conditions. There is a
59// "disconnected scenario," and a "really fast startup and shutdown" scenario.
60// In the "never connected" situation, the user has (during the running of the
61// process) never established an internet connection. As a result, attempts to
62// transmit the initial log have failed, and a lot(?) of data has accumulated in
63// the ongoing log (which didn't yet get closed, because there was never even a
64// contemplation of sending it). There is also a kindred "lost connection"
65// situation, where a loss of connection prevented an ongoing log from being
66// transmitted, and a (still open) log was stuck accumulating a lot(?) of data,
67// while the earlier log retried its transmission. In both of these
68// disconnected situations, two logs need to be, and are, persistently stored
69// for future transmission.
70//
71// The other unusual shutdown condition, termed "really fast startup and
72// shutdown," involves the deliberate user termination of the process before
73// the initial log is even formed or transmitted. In that situation, no logging
74// is done, but the historical crash statistics remain (unlogged) for inclusion
75// in a future run's initial log. (i.e., we don't lose crash stats).
76//
77// With the above overview, we can now describe the state machine's various
78// stats, based on the State enum specified in the state_ member. Those states
79// are:
80//
81// INITIALIZED, // Constructor was called.
[email protected]85ed9d42010-06-08 22:37:4482// INIT_TASK_SCHEDULED, // Waiting for deferred init tasks to complete.
83// INIT_TASK_DONE, // Waiting for timer to send initial log.
initial.commit09911bf2008-07-26 23:55:2984// INITIAL_LOG_READY, // Initial log generated, and waiting for reply.
85// SEND_OLD_INITIAL_LOGS, // Sending unsent logs from previous session.
86// SENDING_OLD_LOGS, // Sending unsent logs from previous session.
87// SENDING_CURRENT_LOGS, // Sending standard current logs as they accrue.
88//
89// In more detail, we have:
90//
91// INITIALIZED, // Constructor was called.
92// The MS has been constructed, but has taken no actions to compose the
93// initial log.
94//
[email protected]85ed9d42010-06-08 22:37:4495// INIT_TASK_SCHEDULED, // Waiting for deferred init tasks to complete.
initial.commit09911bf2008-07-26 23:55:2996// Typically about 30 seconds after startup, a task is sent to a second thread
[email protected]85ed9d42010-06-08 22:37:4497// (the file thread) to perform deferred (lower priority and slower)
98// initialization steps such as getting the list of plugins. That task will
99// (when complete) make an async callback (via a Task) to indicate the
100// completion.
initial.commit09911bf2008-07-26 23:55:29101//
[email protected]85ed9d42010-06-08 22:37:44102// INIT_TASK_DONE, // Waiting for timer to send initial log.
initial.commit09911bf2008-07-26 23:55:29103// The callback has arrived, and it is now possible for an initial log to be
104// created. This callback typically arrives back less than one second after
[email protected]85ed9d42010-06-08 22:37:44105// the deferred init task is dispatched.
initial.commit09911bf2008-07-26 23:55:29106//
107// INITIAL_LOG_READY, // Initial log generated, and waiting for reply.
108// This state is entered only after an initial log has been composed, and
109// prepared for transmission. It is also the case that any previously unsent
110// logs have been loaded into instance variables for possible transmission.
111//
112// SEND_OLD_INITIAL_LOGS, // Sending unsent logs from previous session.
113// This state indicates that the initial log for this session has been
114// successfully sent and it is now time to send any "initial logs" that were
115// saved from previous sessions. Most commonly, there are none, but all old
116// logs that were "initial logs" must be sent before this state is exited.
117//
118// SENDING_OLD_LOGS, // Sending unsent logs from previous session.
119// This state indicates that there are no more unsent initial logs, and now any
120// ongoing logs from previous sessions should be transmitted. All such logs
121// will be transmitted before exiting this state, and proceeding with ongoing
122// logs from the current session (see next state).
123//
124// SENDING_CURRENT_LOGS, // Sending standard current logs as they accrue.
[email protected]0b33f80b2008-12-17 21:34:36125// Current logs are being accumulated. Typically every 20 minutes a log is
initial.commit09911bf2008-07-26 23:55:29126// closed and finalized for transmission, at the same time as a new log is
127// started.
128//
129// The progression through the above states is simple, and sequential, in the
130// most common use cases. States proceed from INITIAL to SENDING_CURRENT_LOGS,
131// and remain in the latter until shutdown.
132//
133// The one unusual case is when the user asks that we stop logging. When that
134// happens, any pending (transmission in progress) log is pushed into the list
135// of old unsent logs (the appropriate list, depending on whether it is an
136// initial log, or an ongoing log). An addition, any log that is currently
137// accumulating is also finalized, and pushed into the unsent log list. With
[email protected]281d2882009-01-20 20:32:42138// those pushes performed, we regress back to the SEND_OLD_INITIAL_LOGS state in
initial.commit09911bf2008-07-26 23:55:29139// case the user enables log recording again during this session. This way
140// anything we have "pushed back" will be sent automatically if/when we progress
141// back to SENDING_CURRENT_LOG state.
142//
143// Also note that whenever the member variables containing unsent logs are
144// modified (i.e., when we send an old log), we mirror the list of logs into
145// the PrefServices. This ensures that IF we crash, we won't start up and
146// retransmit our old logs again.
147//
148// Due to race conditions, it is always possible that a log file could be sent
149// twice. For example, if a log file is sent, but not yet acknowledged by
150// the external server, and the user shuts down, then a copy of the log may be
151// saved for re-transmission. These duplicates could be filtered out server
[email protected]281d2882009-01-20 20:32:42152// side, but are not expected to be a significant problem.
initial.commit09911bf2008-07-26 23:55:29153//
154//
155//------------------------------------------------------------------------------
156
[email protected]40bcc302009-03-02 20:50:39157#include "chrome/browser/metrics/metrics_service.h"
158
[email protected]46f89e142010-07-19 08:00:42159#include "base/base64.h"
[email protected]7f7f1962011-04-20 15:58:16160#include "base/bind.h"
161#include "base/callback.h"
[email protected]5d91c9e2010-07-28 17:25:28162#include "base/command_line.h"
[email protected]46f89e142010-07-19 08:00:42163#include "base/md5.h"
[email protected]835d7c82010-10-14 04:38:38164#include "base/metrics/histogram.h"
[email protected]528c56d2010-07-30 19:28:44165#include "base/string_number_conversions.h"
[email protected]ce072a72010-12-31 20:02:16166#include "base/threading/platform_thread.h"
[email protected]b3841c502011-03-09 01:21:31167#include "base/threading/thread.h"
[email protected]440b37b22010-08-30 05:31:40168#include "base/utf_string_conversions.h"
[email protected]679082052010-07-21 21:30:13169#include "base/values.h"
[email protected]d8e41ed2008-09-11 15:22:32170#include "chrome/browser/bookmarks/bookmark_model.h"
initial.commit09911bf2008-07-26 23:55:29171#include "chrome/browser/browser_process.h"
172#include "chrome/browser/load_notification_details.h"
[email protected]84c988a2011-04-19 17:56:33173#include "chrome/browser/memory_details.h"
[email protected]7c927b62010-02-24 09:54:13174#include "chrome/browser/metrics/histogram_synchronizer.h"
[email protected]679082052010-07-21 21:30:13175#include "chrome/browser/metrics/metrics_log.h"
[email protected]7f7f1962011-04-20 15:58:16176#include "chrome/browser/metrics/metrics_reporting_scheduler.h"
[email protected]37858e52010-08-26 00:22:02177#include "chrome/browser/prefs/pref_service.h"
[email protected]f8628c22011-04-05 12:10:18178#include "chrome/browser/prefs/scoped_user_pref_update.h"
[email protected]8ecad5e2010-12-02 21:18:33179#include "chrome/browser/profiles/profile.h"
[email protected]d54e03a52009-01-16 00:31:04180#include "chrome/browser/search_engines/template_url_model.h"
[email protected]71b73f02011-04-06 15:57:29181#include "chrome/browser/ui/browser_list.h"
[email protected]157d5472009-11-05 22:31:03182#include "chrome/common/child_process_logging.h"
[email protected]92745242009-06-12 16:52:21183#include "chrome/common/chrome_switches.h"
[email protected]3eb0d8f72010-12-15 23:38:25184#include "chrome/common/guid.h"
initial.commit09911bf2008-07-26 23:55:29185#include "chrome/common/pref_names.h"
[email protected]e09ba552009-02-05 03:26:29186#include "chrome/common/render_messages.h"
[email protected]5de634712011-03-02 00:20:19187#include "content/browser/renderer_host/render_process_host.h"
[email protected]6faf7b12011-03-09 19:24:14188#include "content/common/child_process_info.h"
[email protected]b3841c502011-03-09 01:21:31189#include "content/common/notification_service.h"
[email protected]191eb3f72010-12-21 06:27:50190#include "webkit/plugins/npapi/plugin_list.h"
191#include "webkit/plugins/npapi/webplugininfo.h"
initial.commit09911bf2008-07-26 23:55:29192
[email protected]e06131d2010-02-10 18:40:33193// TODO(port): port browser_distribution.h.
194#if !defined(OS_POSIX)
[email protected]79bf0b72009-04-27 21:30:55195#include "chrome/installer/util/browser_distribution.h"
[email protected]dc6f4962009-02-13 01:25:50196#endif
197
[email protected]5ccaa412009-11-13 22:00:16198#if defined(OS_CHROMEOS)
[email protected]db342d52010-08-09 21:19:37199#include "chrome/browser/chromeos/cros/cros_library.h"
[email protected]5ccaa412009-11-13 22:00:16200#include "chrome/browser/chromeos/external_metrics.h"
[email protected]9b345162011-04-15 05:21:30201#include "chrome/browser/chromeos/system_access.h"
[email protected]5ccaa412009-11-13 22:00:16202#endif
203
[email protected]46f89e142010-07-19 08:00:42204namespace {
205MetricsService::LogRecallStatus MakeRecallStatusHistogram(
206 MetricsService::LogRecallStatus status) {
207 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecall", status,
208 MetricsService::END_RECALL_STATUS);
209 return status;
210}
211
212// TODO(ziadh): Remove this when done with experiment.
213void MakeStoreStatusHistogram(MetricsService::LogStoreStatus status) {
[email protected]4e95d202010-07-24 01:47:56214 UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogStore2", status,
[email protected]46f89e142010-07-19 08:00:42215 MetricsService::END_STORE_STATUS);
216}
217} // namespace
218
[email protected]e1acf6f2008-10-27 20:43:33219using base::Time;
[email protected]e1acf6f2008-10-27 20:43:33220
initial.commit09911bf2008-07-26 23:55:29221// Check to see that we're being called on only one thread.
222static bool IsSingleThreaded();
223
initial.commit09911bf2008-07-26 23:55:29224static const char kMetricsType[] = "application/vnd.mozilla.metrics.bz2";
225
[email protected]7f7f1962011-04-20 15:58:16226// The delay, in seconds, after starting recording before doing expensive
227// initialization work.
228static const int kInitializationDelaySeconds = 30;
[email protected]252873ef2008-08-04 21:59:45229
[email protected]c9a3ef82009-05-28 22:02:46230// This specifies the amount of time to wait for all renderers to send their
231// data.
232static const int kMaxHistogramGatheringWaitDuration = 60000; // 60 seconds.
233
[email protected]54702c92011-04-15 15:06:43234// The maximum number of events in a log uploaded to the UMA server.
235static const int kEventLimit = 2400;
[email protected]68475e602008-08-22 03:21:15236
237// If an upload fails, and the transmission was over this byte count, then we
238// will discard the log, and not try to retransmit it. We also don't persist
239// the log to the prefs for transmission during the next chrome session if this
240// limit is exceeded.
241static const int kUploadLogAvoidRetransmitSize = 50000;
initial.commit09911bf2008-07-26 23:55:29242
initial.commit09911bf2008-07-26 23:55:29243// Interval, in seconds, between state saves.
244static const int kSaveStateInterval = 5 * 60; // five minutes
245
246// The number of "initial" logs we're willing to save, and hope to send during
247// a future Chrome session. Initial logs contain crash stats, and are pretty
248// small.
249static const size_t kMaxInitialLogsPersisted = 20;
250
251// The number of ongoing logs we're willing to save persistently, and hope to
[email protected]281d2882009-01-20 20:32:42252// send during a this or future sessions. Note that each log may be pretty
initial.commit09911bf2008-07-26 23:55:29253// large, as presumably the related "initial" log wasn't sent (probably nothing
254// was, as the user was probably off-line). As a result, the log probably kept
255// accumulating while the "initial" log was stalled (pending_), and couldn't be
256// sent. As a result, we don't want to save too many of these mega-logs.
257// A "standard shutdown" will create a small log, including just the data that
258// was not yet been transmitted, and that is normal (to have exactly one
259// ongoing_log_ at startup).
[email protected]281d2882009-01-20 20:32:42260static const size_t kMaxOngoingLogsPersisted = 8;
initial.commit09911bf2008-07-26 23:55:29261
[email protected]46f89e142010-07-19 08:00:42262// We append (2) more elements to persisted lists: the size of the list and a
263// checksum of the elements.
264static const size_t kChecksumEntryCount = 2;
265
[email protected]679082052010-07-21 21:30:13266// This is used to quickly log stats from child process related notifications in
267// MetricsService::child_stats_buffer_. The buffer's contents are transferred
268// out when Local State is periodically saved. The information is then
269// reported to the UMA server on next launch.
270struct MetricsService::ChildProcessStats {
271 public:
272 explicit ChildProcessStats(ChildProcessInfo::ProcessType type)
273 : process_launches(0),
274 process_crashes(0),
275 instances(0),
276 process_type(type) {}
277
278 // This constructor is only used by the map to return some default value for
279 // an index for which no value has been assigned.
280 ChildProcessStats()
281 : process_launches(0),
282 process_crashes(0),
283 instances(0),
284 process_type(ChildProcessInfo::UNKNOWN_PROCESS) {}
285
286 // The number of times that the given child process has been launched
287 int process_launches;
288
289 // The number of times that the given child process has crashed
290 int process_crashes;
291
292 // The number of instances of this child process that have been created.
293 // An instance is a DOM object rendered by this child process during a page
294 // load.
295 int instances;
296
297 ChildProcessInfo::ProcessType process_type;
298};
initial.commit09911bf2008-07-26 23:55:29299
[email protected]84c988a2011-04-19 17:56:33300// Handles asynchronous fetching of memory details.
301// Will run the provided task after finished.
302class MetricsMemoryDetails : public MemoryDetails {
303 public:
304 explicit MetricsMemoryDetails(Task* completion) : completion_(completion) {}
305
306 virtual void OnDetailsAvailable() {
307 MessageLoop::current()->PostTask(FROM_HERE, completion_);
308 }
309
310 private:
311 ~MetricsMemoryDetails() {}
312
313 Task* completion_;
314 DISALLOW_COPY_AND_ASSIGN(MetricsMemoryDetails);
315};
316
[email protected]85ed9d42010-06-08 22:37:44317class MetricsService::InitTaskComplete : public Task {
[email protected]35fa6a22009-08-15 00:04:01318 public:
[email protected]191eb3f72010-12-21 06:27:50319 explicit InitTaskComplete(
320 const std::string& hardware_class,
321 const std::vector<webkit::npapi::WebPluginInfo>& plugins)
[email protected]85ed9d42010-06-08 22:37:44322 : hardware_class_(hardware_class), plugins_(plugins) {}
323
[email protected]7f2e792e2009-11-30 23:18:29324 virtual void Run() {
[email protected]85ed9d42010-06-08 22:37:44325 g_browser_process->metrics_service()->OnInitTaskComplete(
326 hardware_class_, plugins_);
initial.commit09911bf2008-07-26 23:55:29327 }
[email protected]35fa6a22009-08-15 00:04:01328
[email protected]7f2e792e2009-11-30 23:18:29329 private:
[email protected]85ed9d42010-06-08 22:37:44330 std::string hardware_class_;
[email protected]191eb3f72010-12-21 06:27:50331 std::vector<webkit::npapi::WebPluginInfo> plugins_;
initial.commit09911bf2008-07-26 23:55:29332};
333
[email protected]85ed9d42010-06-08 22:37:44334class MetricsService::InitTask : public Task {
[email protected]7f2e792e2009-11-30 23:18:29335 public:
[email protected]85ed9d42010-06-08 22:37:44336 explicit InitTask(MessageLoop* callback_loop)
[email protected]7f2e792e2009-11-30 23:18:29337 : callback_loop_(callback_loop) {}
338
339 virtual void Run() {
[email protected]191eb3f72010-12-21 06:27:50340 std::vector<webkit::npapi::WebPluginInfo> plugins;
341 webkit::npapi::PluginList::Singleton()->GetPlugins(false, &plugins);
[email protected]85ed9d42010-06-08 22:37:44342 std::string hardware_class; // Empty string by default.
343#if defined(OS_CHROMEOS)
[email protected]9b345162011-04-15 05:21:30344 chromeos::SystemAccess::GetInstance()->GetMachineStatistic(
345 "hardware_class", &hardware_class);
[email protected]85ed9d42010-06-08 22:37:44346#endif // OS_CHROMEOS
347 callback_loop_->PostTask(FROM_HERE, new InitTaskComplete(
348 hardware_class, plugins));
[email protected]7f2e792e2009-11-30 23:18:29349 }
350
351 private:
352 MessageLoop* callback_loop_;
353};
[email protected]90d41372009-11-30 21:52:32354
initial.commit09911bf2008-07-26 23:55:29355// static
356void MetricsService::RegisterPrefs(PrefService* local_state) {
357 DCHECK(IsSingleThreaded());
[email protected]20ce516d2010-06-18 02:20:04358 local_state->RegisterStringPref(prefs::kMetricsClientID, "");
[email protected]0bb1a622009-03-04 03:22:32359 local_state->RegisterInt64Pref(prefs::kMetricsClientIDTimestamp, 0);
360 local_state->RegisterInt64Pref(prefs::kStabilityLaunchTimeSec, 0);
361 local_state->RegisterInt64Pref(prefs::kStabilityLastTimestampSec, 0);
[email protected]20ce516d2010-06-18 02:20:04362 local_state->RegisterStringPref(prefs::kStabilityStatsVersion, "");
[email protected]225c50842010-01-19 21:19:13363 local_state->RegisterInt64Pref(prefs::kStabilityStatsBuildTime, 0);
initial.commit09911bf2008-07-26 23:55:29364 local_state->RegisterBooleanPref(prefs::kStabilityExitedCleanly, true);
365 local_state->RegisterBooleanPref(prefs::kStabilitySessionEndCompleted, true);
366 local_state->RegisterIntegerPref(prefs::kMetricsSessionID, -1);
367 local_state->RegisterIntegerPref(prefs::kStabilityLaunchCount, 0);
368 local_state->RegisterIntegerPref(prefs::kStabilityCrashCount, 0);
369 local_state->RegisterIntegerPref(prefs::kStabilityIncompleteSessionEndCount,
370 0);
371 local_state->RegisterIntegerPref(prefs::kStabilityPageLoadCount, 0);
initial.commit09911bf2008-07-26 23:55:29372 local_state->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0);
[email protected]1f085622009-12-04 05:33:45373 local_state->RegisterIntegerPref(prefs::kStabilityExtensionRendererCrashCount,
374 0);
initial.commit09911bf2008-07-26 23:55:29375 local_state->RegisterIntegerPref(prefs::kStabilityRendererHangCount, 0);
[email protected]1f085622009-12-04 05:33:45376 local_state->RegisterIntegerPref(prefs::kStabilityChildProcessCrashCount, 0);
[email protected]e73c01972008-08-13 00:18:24377 local_state->RegisterIntegerPref(prefs::kStabilityBreakpadRegistrationFail,
378 0);
379 local_state->RegisterIntegerPref(prefs::kStabilityBreakpadRegistrationSuccess,
380 0);
381 local_state->RegisterIntegerPref(prefs::kStabilityDebuggerPresent, 0);
382 local_state->RegisterIntegerPref(prefs::kStabilityDebuggerNotPresent, 0);
[email protected]c1834a92011-01-21 18:21:03383#if defined(OS_CHROMEOS)
384 local_state->RegisterIntegerPref(prefs::kStabilityOtherUserCrashCount, 0);
385 local_state->RegisterIntegerPref(prefs::kStabilityKernelCrashCount, 0);
386 local_state->RegisterIntegerPref(prefs::kStabilitySystemUncleanShutdownCount,
387 0);
388#endif // OS_CHROMEOS
[email protected]e73c01972008-08-13 00:18:24389
initial.commit09911bf2008-07-26 23:55:29390 local_state->RegisterDictionaryPref(prefs::kProfileMetrics);
391 local_state->RegisterIntegerPref(prefs::kNumBookmarksOnBookmarkBar, 0);
392 local_state->RegisterIntegerPref(prefs::kNumFoldersOnBookmarkBar, 0);
393 local_state->RegisterIntegerPref(prefs::kNumBookmarksInOtherBookmarkFolder,
394 0);
395 local_state->RegisterIntegerPref(prefs::kNumFoldersInOtherBookmarkFolder, 0);
396 local_state->RegisterIntegerPref(prefs::kNumKeywords, 0);
397 local_state->RegisterListPref(prefs::kMetricsInitialLogs);
398 local_state->RegisterListPref(prefs::kMetricsOngoingLogs);
[email protected]0bb1a622009-03-04 03:22:32399
400 local_state->RegisterInt64Pref(prefs::kUninstallMetricsPageLoadCount, 0);
401 local_state->RegisterInt64Pref(prefs::kUninstallLaunchCount, 0);
[email protected]6b5f21d2009-04-13 17:01:35402 local_state->RegisterInt64Pref(prefs::kUninstallMetricsInstallDate, 0);
[email protected]0bb1a622009-03-04 03:22:32403 local_state->RegisterInt64Pref(prefs::kUninstallMetricsUptimeSec, 0);
404 local_state->RegisterInt64Pref(prefs::kUninstallLastLaunchTimeSec, 0);
405 local_state->RegisterInt64Pref(prefs::kUninstallLastObservedRunTimeSec, 0);
initial.commit09911bf2008-07-26 23:55:29406}
407
[email protected]541f77922009-02-23 21:14:38408// static
409void MetricsService::DiscardOldStabilityStats(PrefService* local_state) {
410 local_state->SetBoolean(prefs::kStabilityExitedCleanly, true);
[email protected]c9abf242009-07-18 06:00:38411 local_state->SetBoolean(prefs::kStabilitySessionEndCompleted, true);
[email protected]541f77922009-02-23 21:14:38412
413 local_state->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0);
414 local_state->SetInteger(prefs::kStabilityBreakpadRegistrationSuccess, 0);
415 local_state->SetInteger(prefs::kStabilityBreakpadRegistrationFail, 0);
416 local_state->SetInteger(prefs::kStabilityDebuggerPresent, 0);
417 local_state->SetInteger(prefs::kStabilityDebuggerNotPresent, 0);
418
419 local_state->SetInteger(prefs::kStabilityLaunchCount, 0);
420 local_state->SetInteger(prefs::kStabilityCrashCount, 0);
421
422 local_state->SetInteger(prefs::kStabilityPageLoadCount, 0);
423 local_state->SetInteger(prefs::kStabilityRendererCrashCount, 0);
424 local_state->SetInteger(prefs::kStabilityRendererHangCount, 0);
425
[email protected]9165f742010-03-10 22:55:01426 local_state->SetInt64(prefs::kStabilityLaunchTimeSec, 0);
427 local_state->SetInt64(prefs::kStabilityLastTimestampSec, 0);
[email protected]541f77922009-02-23 21:14:38428
429 local_state->ClearPref(prefs::kStabilityPluginStats);
[email protected]ae155cb92009-06-19 06:10:37430
[email protected]f8628c22011-04-05 12:10:18431 local_state->ClearPref(prefs::kMetricsInitialLogs);
432 local_state->ClearPref(prefs::kMetricsOngoingLogs);
[email protected]541f77922009-02-23 21:14:38433}
434
initial.commit09911bf2008-07-26 23:55:29435MetricsService::MetricsService()
[email protected]d01b8732008-10-16 02:18:07436 : recording_active_(false),
437 reporting_active_(false),
[email protected]d01b8732008-10-16 02:18:07438 state_(INITIALIZED),
initial.commit09911bf2008-07-26 23:55:29439 current_fetch_(NULL),
[email protected]d01b8732008-10-16 02:18:07440 idle_since_last_transmission_(false),
initial.commit09911bf2008-07-26 23:55:29441 next_window_id_(0),
[email protected]40bcc302009-03-02 20:50:39442 ALLOW_THIS_IN_INITIALIZER_LIST(log_sender_factory_(this)),
443 ALLOW_THIS_IN_INITIALIZER_LIST(state_saver_factory_(this)),
[email protected]7f7f1962011-04-20 15:58:16444 waiting_for_asynchronus_reporting_step_(false) {
initial.commit09911bf2008-07-26 23:55:29445 DCHECK(IsSingleThreaded());
446 InitializeMetricsState();
[email protected]7f7f1962011-04-20 15:58:16447
448 base::Closure callback = base::Bind(&MetricsService::StartScheduledUpload,
449 base::Unretained(this));
450 scheduler_.reset(new MetricsReportingScheduler(callback));
initial.commit09911bf2008-07-26 23:55:29451}
452
453MetricsService::~MetricsService() {
454 SetRecording(false);
455}
456
[email protected]d01b8732008-10-16 02:18:07457void MetricsService::Start() {
[email protected]b1c8dc02011-04-13 18:32:04458 HandleIdleSinceLastTransmission(false);
[email protected]d01b8732008-10-16 02:18:07459 SetRecording(true);
460 SetReporting(true);
461}
462
463void MetricsService::StartRecordingOnly() {
464 SetRecording(true);
465 SetReporting(false);
466}
467
468void MetricsService::Stop() {
[email protected]b1c8dc02011-04-13 18:32:04469 HandleIdleSinceLastTransmission(false);
[email protected]d01b8732008-10-16 02:18:07470 SetReporting(false);
471 SetRecording(false);
472}
473
initial.commit09911bf2008-07-26 23:55:29474void MetricsService::SetRecording(bool enabled) {
475 DCHECK(IsSingleThreaded());
476
[email protected]d01b8732008-10-16 02:18:07477 if (enabled == recording_active_)
initial.commit09911bf2008-07-26 23:55:29478 return;
479
480 if (enabled) {
[email protected]b0c819f2009-03-08 04:52:15481 if (client_id_.empty()) {
482 PrefService* pref = g_browser_process->local_state();
483 DCHECK(pref);
[email protected]ddd231e2010-06-29 20:35:19484 client_id_ = pref->GetString(prefs::kMetricsClientID);
[email protected]b0c819f2009-03-08 04:52:15485 if (client_id_.empty()) {
486 client_id_ = GenerateClientID();
[email protected]ddd231e2010-06-29 20:35:19487 pref->SetString(prefs::kMetricsClientID, client_id_);
[email protected]b0c819f2009-03-08 04:52:15488
489 // Might as well make a note of how long this ID has existed
490 pref->SetString(prefs::kMetricsClientIDTimestamp,
[email protected]528c56d2010-07-30 19:28:44491 base::Int64ToString(Time::Now().ToTimeT()));
[email protected]b0c819f2009-03-08 04:52:15492 }
493 }
[email protected]157d5472009-11-05 22:31:03494 child_process_logging::SetClientId(client_id_);
initial.commit09911bf2008-07-26 23:55:29495 StartRecording();
[email protected]005ef3e2009-05-22 20:55:46496
[email protected]3ffd3ae2011-03-17 22:17:52497 SetUpNotifications(&registrar_, this);
initial.commit09911bf2008-07-26 23:55:29498 } else {
[email protected]005ef3e2009-05-22 20:55:46499 registrar_.RemoveAll();
initial.commit09911bf2008-07-26 23:55:29500 PushPendingLogsToUnsentLists();
501 DCHECK(!pending_log());
502 if (state_ > INITIAL_LOG_READY && unsent_logs())
503 state_ = SEND_OLD_INITIAL_LOGS;
504 }
[email protected]d01b8732008-10-16 02:18:07505 recording_active_ = enabled;
initial.commit09911bf2008-07-26 23:55:29506}
507
[email protected]d01b8732008-10-16 02:18:07508bool MetricsService::recording_active() const {
initial.commit09911bf2008-07-26 23:55:29509 DCHECK(IsSingleThreaded());
[email protected]d01b8732008-10-16 02:18:07510 return recording_active_;
initial.commit09911bf2008-07-26 23:55:29511}
512
[email protected]d01b8732008-10-16 02:18:07513void MetricsService::SetReporting(bool enable) {
514 if (reporting_active_ != enable) {
515 reporting_active_ = enable;
516 if (reporting_active_)
[email protected]7f7f1962011-04-20 15:58:16517 StartSchedulerIfNecessary();
initial.commit09911bf2008-07-26 23:55:29518 }
[email protected]d01b8732008-10-16 02:18:07519}
520
521bool MetricsService::reporting_active() const {
522 DCHECK(IsSingleThreaded());
523 return reporting_active_;
initial.commit09911bf2008-07-26 23:55:29524}
525
[email protected]87ef9ea2011-02-26 03:15:15526// static
[email protected]3ffd3ae2011-03-17 22:17:52527void MetricsService::SetUpNotifications(NotificationRegistrar* registrar,
[email protected]87ef9ea2011-02-26 03:15:15528 NotificationObserver* observer) {
529 registrar->Add(observer, NotificationType::BROWSER_OPENED,
530 NotificationService::AllSources());
531 registrar->Add(observer, NotificationType::BROWSER_CLOSED,
532 NotificationService::AllSources());
533 registrar->Add(observer, NotificationType::USER_ACTION,
534 NotificationService::AllSources());
535 registrar->Add(observer, NotificationType::TAB_PARENTED,
536 NotificationService::AllSources());
537 registrar->Add(observer, NotificationType::TAB_CLOSING,
538 NotificationService::AllSources());
539 registrar->Add(observer, NotificationType::LOAD_START,
540 NotificationService::AllSources());
541 registrar->Add(observer, NotificationType::LOAD_STOP,
542 NotificationService::AllSources());
543 registrar->Add(observer, NotificationType::RENDERER_PROCESS_CLOSED,
544 NotificationService::AllSources());
545 registrar->Add(observer, NotificationType::RENDERER_PROCESS_HANG,
546 NotificationService::AllSources());
547 registrar->Add(observer, NotificationType::CHILD_PROCESS_HOST_CONNECTED,
548 NotificationService::AllSources());
549 registrar->Add(observer, NotificationType::CHILD_INSTANCE_CREATED,
550 NotificationService::AllSources());
551 registrar->Add(observer, NotificationType::CHILD_PROCESS_CRASHED,
552 NotificationService::AllSources());
553 registrar->Add(observer, NotificationType::TEMPLATE_URL_MODEL_LOADED,
554 NotificationService::AllSources());
555 registrar->Add(observer, NotificationType::OMNIBOX_OPENED_URL,
556 NotificationService::AllSources());
557 registrar->Add(observer, NotificationType::BOOKMARK_MODEL_LOADED,
558 NotificationService::AllSources());
559}
560
initial.commit09911bf2008-07-26 23:55:29561void MetricsService::Observe(NotificationType type,
562 const NotificationSource& source,
563 const NotificationDetails& details) {
564 DCHECK(current_log_);
565 DCHECK(IsSingleThreaded());
566
567 if (!CanLogNotification(type, source, details))
568 return;
569
[email protected]bfd04a62009-02-01 18:16:56570 switch (type.value) {
571 case NotificationType::USER_ACTION:
[email protected]afe3a1672009-11-17 19:04:12572 current_log_->RecordUserAction(*Details<const char*>(details).ptr());
initial.commit09911bf2008-07-26 23:55:29573 break;
574
[email protected]bfd04a62009-02-01 18:16:56575 case NotificationType::BROWSER_OPENED:
576 case NotificationType::BROWSER_CLOSED:
initial.commit09911bf2008-07-26 23:55:29577 LogWindowChange(type, source, details);
578 break;
579
[email protected]bfd04a62009-02-01 18:16:56580 case NotificationType::TAB_PARENTED:
581 case NotificationType::TAB_CLOSING:
initial.commit09911bf2008-07-26 23:55:29582 LogWindowChange(type, source, details);
583 break;
584
[email protected]bfd04a62009-02-01 18:16:56585 case NotificationType::LOAD_STOP:
initial.commit09911bf2008-07-26 23:55:29586 LogLoadComplete(type, source, details);
587 break;
588
[email protected]bfd04a62009-02-01 18:16:56589 case NotificationType::LOAD_START:
initial.commit09911bf2008-07-26 23:55:29590 LogLoadStarted();
591 break;
592
[email protected]443b80e2010-12-14 00:42:23593 case NotificationType::RENDERER_PROCESS_CLOSED: {
[email protected]cd69619b2010-05-05 02:41:38594 RenderProcessHost::RendererClosedDetails* process_details =
595 Details<RenderProcessHost::RendererClosedDetails>(details).ptr();
[email protected]443b80e2010-12-14 00:42:23596 if (process_details->status ==
597 base::TERMINATION_STATUS_PROCESS_CRASHED ||
598 process_details->status ==
599 base::TERMINATION_STATUS_ABNORMAL_TERMINATION) {
[email protected]cd69619b2010-05-05 02:41:38600 if (process_details->was_extension_renderer) {
601 LogExtensionRendererCrash();
602 } else {
603 LogRendererCrash();
604 }
605 }
[email protected]1f085622009-12-04 05:33:45606 }
initial.commit09911bf2008-07-26 23:55:29607 break;
608
[email protected]bfd04a62009-02-01 18:16:56609 case NotificationType::RENDERER_PROCESS_HANG:
initial.commit09911bf2008-07-26 23:55:29610 LogRendererHang();
611 break;
612
[email protected]a27a9382009-02-11 23:55:10613 case NotificationType::CHILD_PROCESS_HOST_CONNECTED:
614 case NotificationType::CHILD_PROCESS_CRASHED:
615 case NotificationType::CHILD_INSTANCE_CREATED:
616 LogChildProcessChange(type, source, details);
initial.commit09911bf2008-07-26 23:55:29617 break;
618
[email protected]bfd04a62009-02-01 18:16:56619 case NotificationType::TEMPLATE_URL_MODEL_LOADED:
initial.commit09911bf2008-07-26 23:55:29620 LogKeywords(Source<TemplateURLModel>(source).ptr());
621 break;
622
[email protected]1226abb2010-06-10 18:01:28623 case NotificationType::OMNIBOX_OPENED_URL: {
624 MetricsLog* current_log = current_log_->AsMetricsLog();
625 DCHECK(current_log);
626 current_log->RecordOmniboxOpenedURL(
initial.commit09911bf2008-07-26 23:55:29627 *Details<AutocompleteLog>(details).ptr());
628 break;
[email protected]1226abb2010-06-10 18:01:28629 }
initial.commit09911bf2008-07-26 23:55:29630
[email protected]b61236c62009-04-09 22:43:55631 case NotificationType::BOOKMARK_MODEL_LOADED: {
632 Profile* p = Source<Profile>(source).ptr();
633 if (p)
634 LogBookmarks(p->GetBookmarkModel());
initial.commit09911bf2008-07-26 23:55:29635 break;
[email protected]b61236c62009-04-09 22:43:55636 }
initial.commit09911bf2008-07-26 23:55:29637 default:
[email protected]a063c102010-07-22 22:20:19638 NOTREACHED();
initial.commit09911bf2008-07-26 23:55:29639 break;
640 }
[email protected]d01b8732008-10-16 02:18:07641
642 HandleIdleSinceLastTransmission(false);
643
644 if (current_log_)
[email protected]666205032010-10-21 20:56:58645 DVLOG(1) << "METRICS: NUMBER OF EVENTS = " << current_log_->num_events();
[email protected]d01b8732008-10-16 02:18:07646}
647
648void MetricsService::HandleIdleSinceLastTransmission(bool in_idle) {
649 // If there wasn't a lot of action, maybe the computer was asleep, in which
650 // case, the log transmissions should have stopped. Here we start them up
651 // again.
[email protected]cac78842008-11-27 01:02:20652 if (!in_idle && idle_since_last_transmission_)
[email protected]7f7f1962011-04-20 15:58:16653 StartSchedulerIfNecessary();
[email protected]cac78842008-11-27 01:02:20654 idle_since_last_transmission_ = in_idle;
initial.commit09911bf2008-07-26 23:55:29655}
656
initial.commit09911bf2008-07-26 23:55:29657void MetricsService::RecordStartOfSessionEnd() {
[email protected]466f3c12011-03-23 21:20:38658 LogCleanShutdown();
initial.commit09911bf2008-07-26 23:55:29659 RecordBooleanPrefValue(prefs::kStabilitySessionEndCompleted, false);
660}
661
662void MetricsService::RecordCompletedSessionEnd() {
[email protected]466f3c12011-03-23 21:20:38663 LogCleanShutdown();
initial.commit09911bf2008-07-26 23:55:29664 RecordBooleanPrefValue(prefs::kStabilitySessionEndCompleted, true);
665}
666
[email protected]7f7f1962011-04-20 15:58:16667void MetricsService::RecordBreakpadRegistration(bool success) {
[email protected]68475e602008-08-22 03:21:15668 if (!success)
[email protected]e73c01972008-08-13 00:18:24669 IncrementPrefValue(prefs::kStabilityBreakpadRegistrationFail);
670 else
671 IncrementPrefValue(prefs::kStabilityBreakpadRegistrationSuccess);
672}
673
674void MetricsService::RecordBreakpadHasDebugger(bool has_debugger) {
675 if (!has_debugger)
676 IncrementPrefValue(prefs::kStabilityDebuggerNotPresent);
677 else
[email protected]68475e602008-08-22 03:21:15678 IncrementPrefValue(prefs::kStabilityDebuggerPresent);
[email protected]e73c01972008-08-13 00:18:24679}
680
initial.commit09911bf2008-07-26 23:55:29681//------------------------------------------------------------------------------
682// private methods
683//------------------------------------------------------------------------------
684
685
686//------------------------------------------------------------------------------
687// Initialization methods
688
689void MetricsService::InitializeMetricsState() {
[email protected]79bf0b72009-04-27 21:30:55690#if defined(OS_POSIX)
691 server_url_ = L"https://clients4.google.com/firefox/metrics/collect";
692#else
693 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
694 server_url_ = dist->GetStatsServerURL();
695#endif
696
initial.commit09911bf2008-07-26 23:55:29697 PrefService* pref = g_browser_process->local_state();
698 DCHECK(pref);
699
[email protected]225c50842010-01-19 21:19:13700 if ((pref->GetInt64(prefs::kStabilityStatsBuildTime)
701 != MetricsLog::GetBuildTime()) ||
[email protected]ddd231e2010-06-29 20:35:19702 (pref->GetString(prefs::kStabilityStatsVersion)
[email protected]225c50842010-01-19 21:19:13703 != MetricsLog::GetVersionString())) {
[email protected]541f77922009-02-23 21:14:38704 // This is a new version, so we don't want to confuse the stats about the
705 // old version with info that we upload.
706 DiscardOldStabilityStats(pref);
707 pref->SetString(prefs::kStabilityStatsVersion,
[email protected]ddd231e2010-06-29 20:35:19708 MetricsLog::GetVersionString());
[email protected]225c50842010-01-19 21:19:13709 pref->SetInt64(prefs::kStabilityStatsBuildTime,
710 MetricsLog::GetBuildTime());
[email protected]541f77922009-02-23 21:14:38711 }
712
initial.commit09911bf2008-07-26 23:55:29713 // Update session ID
714 session_id_ = pref->GetInteger(prefs::kMetricsSessionID);
715 ++session_id_;
716 pref->SetInteger(prefs::kMetricsSessionID, session_id_);
717
initial.commit09911bf2008-07-26 23:55:29718 // Stability bookkeeping
[email protected]e73c01972008-08-13 00:18:24719 IncrementPrefValue(prefs::kStabilityLaunchCount);
initial.commit09911bf2008-07-26 23:55:29720
[email protected]e73c01972008-08-13 00:18:24721 if (!pref->GetBoolean(prefs::kStabilityExitedCleanly)) {
722 IncrementPrefValue(prefs::kStabilityCrashCount);
initial.commit09911bf2008-07-26 23:55:29723 }
[email protected]e73c01972008-08-13 00:18:24724
725 // This will be set to 'true' if we exit cleanly.
initial.commit09911bf2008-07-26 23:55:29726 pref->SetBoolean(prefs::kStabilityExitedCleanly, false);
727
[email protected]e73c01972008-08-13 00:18:24728 if (!pref->GetBoolean(prefs::kStabilitySessionEndCompleted)) {
729 IncrementPrefValue(prefs::kStabilityIncompleteSessionEndCount);
[email protected]c9abf242009-07-18 06:00:38730 // This is marked false when we get a WM_ENDSESSION.
731 pref->SetBoolean(prefs::kStabilitySessionEndCompleted, true);
initial.commit09911bf2008-07-26 23:55:29732 }
initial.commit09911bf2008-07-26 23:55:29733
[email protected]9165f742010-03-10 22:55:01734 // Initialize uptime counters.
735 int64 startup_uptime = MetricsLog::GetIncrementalUptime(pref);
[email protected]ae393ec702010-06-27 16:23:14736 DCHECK_EQ(0, startup_uptime);
[email protected]9165f742010-03-10 22:55:01737 // For backwards compatibility, leave this intact in case Omaha is checking
738 // them. prefs::kStabilityLastTimestampSec may also be useless now.
739 // TODO(jar): Delete these if they have no uses.
[email protected]0bb1a622009-03-04 03:22:32740 pref->SetInt64(prefs::kStabilityLaunchTimeSec, Time::Now().ToTimeT());
741
742 // Bookkeeping for the uninstall metrics.
743 IncrementLongPrefsValue(prefs::kUninstallLaunchCount);
initial.commit09911bf2008-07-26 23:55:29744
745 // Save profile metrics.
746 PrefService* prefs = g_browser_process->local_state();
747 if (prefs) {
748 // Remove the current dictionary and store it for use when sending data to
749 // server. By removing the value we prune potentially dead profiles
750 // (and keys). All valid values are added back once services startup.
751 const DictionaryValue* profile_dictionary =
752 prefs->GetDictionary(prefs::kProfileMetrics);
753 if (profile_dictionary) {
754 // Do a deep copy of profile_dictionary since ClearPref will delete it.
755 profile_dictionary_.reset(static_cast<DictionaryValue*>(
756 profile_dictionary->DeepCopy()));
757 prefs->ClearPref(prefs::kProfileMetrics);
758 }
759 }
760
[email protected]92745242009-06-12 16:52:21761 // Get stats on use of command line.
762 const CommandLine* command_line(CommandLine::ForCurrentProcess());
763 size_t common_commands = 0;
764 if (command_line->HasSwitch(switches::kUserDataDir)) {
765 ++common_commands;
766 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineDatDirCount", 1);
767 }
768
769 if (command_line->HasSwitch(switches::kApp)) {
770 ++common_commands;
771 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineAppModeCount", 1);
772 }
773
774 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineFlagCount",
775 command_line->GetSwitchCount());
776 UMA_HISTOGRAM_COUNTS_100("Chrome.CommandLineUncommonFlagCount",
777 command_line->GetSwitchCount() - common_commands);
778
initial.commit09911bf2008-07-26 23:55:29779 // Kick off the process of saving the state (so the uptime numbers keep
780 // getting updated) every n minutes.
781 ScheduleNextStateSave();
782}
783
[email protected]85ed9d42010-06-08 22:37:44784void MetricsService::OnInitTaskComplete(
785 const std::string& hardware_class,
[email protected]191eb3f72010-12-21 06:27:50786 const std::vector<webkit::npapi::WebPluginInfo>& plugins) {
[email protected]85ed9d42010-06-08 22:37:44787 DCHECK(state_ == INIT_TASK_SCHEDULED);
788 hardware_class_ = hardware_class;
[email protected]35fa6a22009-08-15 00:04:01789 plugins_ = plugins;
[email protected]85ed9d42010-06-08 22:37:44790 if (state_ == INIT_TASK_SCHEDULED)
791 state_ = INIT_TASK_DONE;
initial.commit09911bf2008-07-26 23:55:29792}
793
794std::string MetricsService::GenerateClientID() {
[email protected]3469e7e2010-10-14 20:34:59795 return guid::GenerateGUID();
initial.commit09911bf2008-07-26 23:55:29796}
797
initial.commit09911bf2008-07-26 23:55:29798//------------------------------------------------------------------------------
799// State save methods
800
801void MetricsService::ScheduleNextStateSave() {
802 state_saver_factory_.RevokeAll();
803
804 MessageLoop::current()->PostDelayedTask(FROM_HERE,
805 state_saver_factory_.NewRunnableMethod(&MetricsService::SaveLocalState),
806 kSaveStateInterval * 1000);
807}
808
809void MetricsService::SaveLocalState() {
810 PrefService* pref = g_browser_process->local_state();
811 if (!pref) {
[email protected]a063c102010-07-22 22:20:19812 NOTREACHED();
initial.commit09911bf2008-07-26 23:55:29813 return;
814 }
815
816 RecordCurrentState(pref);
[email protected]6faa0e0d2009-04-28 06:50:36817 pref->ScheduleSavePersistentPrefs();
initial.commit09911bf2008-07-26 23:55:29818
[email protected]281d2882009-01-20 20:32:42819 // TODO(jar): Does this run down the batteries????
initial.commit09911bf2008-07-26 23:55:29820 ScheduleNextStateSave();
821}
822
823
824//------------------------------------------------------------------------------
825// Recording control methods
826
827void MetricsService::StartRecording() {
828 if (current_log_)
829 return;
830
831 current_log_ = new MetricsLog(client_id_, session_id_);
832 if (state_ == INITIALIZED) {
833 // We only need to schedule that run once.
[email protected]85ed9d42010-06-08 22:37:44834 state_ = INIT_TASK_SCHEDULED;
initial.commit09911bf2008-07-26 23:55:29835
[email protected]85ed9d42010-06-08 22:37:44836 // Schedules a task on the file thread for execution of slower
837 // initialization steps (such as plugin list generation) necessary
838 // for sending the initial log. This avoids blocking the main UI
839 // thread.
[email protected]7f2e792e2009-11-30 23:18:29840 g_browser_process->file_thread()->message_loop()->PostDelayedTask(FROM_HERE,
[email protected]85ed9d42010-06-08 22:37:44841 new InitTask(MessageLoop::current()),
[email protected]7f7f1962011-04-20 15:58:16842 kInitializationDelaySeconds * 1000);
initial.commit09911bf2008-07-26 23:55:29843 }
844}
845
[email protected]1226abb2010-06-10 18:01:28846void MetricsService::StopRecording(MetricsLogBase** log) {
initial.commit09911bf2008-07-26 23:55:29847 if (!current_log_)
848 return;
849
[email protected]49cd7e882011-02-01 03:54:36850 current_log_->set_hardware_class(hardware_class_); // Adds to ongoing logs.
[email protected]85ed9d42010-06-08 22:37:44851
[email protected]68475e602008-08-22 03:21:15852 // TODO(jar): Integrate bounds on log recording more consistently, so that we
853 // can stop recording logs that are too big much sooner.
[email protected]54702c92011-04-15 15:06:43854 if (current_log_->num_events() > kEventLimit) {
[email protected]553dba62009-02-24 19:08:23855 UMA_HISTOGRAM_COUNTS("UMA.Discarded Log Events",
[email protected]68475e602008-08-22 03:21:15856 current_log_->num_events());
857 current_log_->CloseLog();
858 delete current_log_;
[email protected]294638782008-09-24 00:22:41859 current_log_ = NULL;
[email protected]68475e602008-08-22 03:21:15860 StartRecording(); // Start trivial log to hold our histograms.
861 }
862
[email protected]0b33f80b2008-12-17 21:34:36863 // Put incremental data (histogram deltas, and realtime stats deltas) at the
[email protected]147bbc0b2009-01-06 19:37:40864 // end of all log transmissions (initial log handles this separately).
initial.commit09911bf2008-07-26 23:55:29865 // Don't bother if we're going to discard current_log_.
[email protected]0b33f80b2008-12-17 21:34:36866 if (log) {
[email protected]49cd7e882011-02-01 03:54:36867 // RecordIncrementalStabilityElements only exists on the derived
868 // MetricsLog class.
869 MetricsLog* current_log = current_log_->AsMetricsLog();
870 DCHECK(current_log);
[email protected]1226abb2010-06-10 18:01:28871 current_log->RecordIncrementalStabilityElements();
initial.commit09911bf2008-07-26 23:55:29872 RecordCurrentHistograms();
[email protected]0b33f80b2008-12-17 21:34:36873 }
initial.commit09911bf2008-07-26 23:55:29874
875 current_log_->CloseLog();
[email protected]cac78842008-11-27 01:02:20876 if (log)
[email protected]49cd7e882011-02-01 03:54:36877 *log = current_log_;
[email protected]cac78842008-11-27 01:02:20878 else
initial.commit09911bf2008-07-26 23:55:29879 delete current_log_;
initial.commit09911bf2008-07-26 23:55:29880 current_log_ = NULL;
881}
882
initial.commit09911bf2008-07-26 23:55:29883void MetricsService::PushPendingLogsToUnsentLists() {
884 if (state_ < INITIAL_LOG_READY)
[email protected]28ab7f92009-01-06 21:39:04885 return; // We didn't and still don't have time to get plugin list etc.
initial.commit09911bf2008-07-26 23:55:29886
887 if (pending_log()) {
888 PreparePendingLogText();
889 if (state_ == INITIAL_LOG_READY) {
890 // We may race here, and send second copy of initial log later.
[email protected]46f89e142010-07-19 08:00:42891 unsent_initial_logs_.push_back(compressed_log_);
[email protected]d01b8732008-10-16 02:18:07892 state_ = SEND_OLD_INITIAL_LOGS;
initial.commit09911bf2008-07-26 23:55:29893 } else {
[email protected]281d2882009-01-20 20:32:42894 // TODO(jar): Verify correctness in other states, including sending unsent
[email protected]541f77922009-02-23 21:14:38895 // initial logs.
[email protected]68475e602008-08-22 03:21:15896 PushPendingLogTextToUnsentOngoingLogs();
initial.commit09911bf2008-07-26 23:55:29897 }
898 DiscardPendingLog();
899 }
900 DCHECK(!pending_log());
901 StopRecording(&pending_log_);
902 PreparePendingLogText();
[email protected]68475e602008-08-22 03:21:15903 PushPendingLogTextToUnsentOngoingLogs();
initial.commit09911bf2008-07-26 23:55:29904 DiscardPendingLog();
905 StoreUnsentLogs();
906}
907
[email protected]68475e602008-08-22 03:21:15908void MetricsService::PushPendingLogTextToUnsentOngoingLogs() {
[email protected]46f89e142010-07-19 08:00:42909 if (compressed_log_.length() >
[email protected]dc6f4962009-02-13 01:25:50910 static_cast<size_t>(kUploadLogAvoidRetransmitSize)) {
[email protected]553dba62009-02-24 19:08:23911 UMA_HISTOGRAM_COUNTS("UMA.Large Accumulated Log Not Persisted",
[email protected]46f89e142010-07-19 08:00:42912 static_cast<int>(compressed_log_.length()));
[email protected]68475e602008-08-22 03:21:15913 return;
914 }
[email protected]46f89e142010-07-19 08:00:42915 unsent_ongoing_logs_.push_back(compressed_log_);
[email protected]68475e602008-08-22 03:21:15916}
917
initial.commit09911bf2008-07-26 23:55:29918//------------------------------------------------------------------------------
919// Transmission of logs methods
920
[email protected]7f7f1962011-04-20 15:58:16921void MetricsService::StartSchedulerIfNecessary() {
922 if (reporting_active() && recording_active())
923 scheduler_->Start();
initial.commit09911bf2008-07-26 23:55:29924}
925
[email protected]7f7f1962011-04-20 15:58:16926void MetricsService::StartScheduledUpload() {
927 // If reporting has been turned off, the scheduler doesn't need to run.
928 if (!reporting_active() || !recording_active()) {
929 scheduler_->Stop();
930 scheduler_->UploadCancelled();
931 return;
932 }
933
934 DCHECK(!waiting_for_asynchronus_reporting_step_);
935 waiting_for_asynchronus_reporting_step_ = true;
936
[email protected]84c988a2011-04-19 17:56:33937 Task* task = log_sender_factory_.
938 NewRunnableMethod(&MetricsService::OnMemoryDetailCollectionDone);
939
940 scoped_refptr<MetricsMemoryDetails> details(new MetricsMemoryDetails(task));
941 details->StartFetch();
942
943 // Collect WebCore cache information to put into a histogram.
944 for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
945 !i.IsAtEnd(); i.Advance())
946 i.GetCurrentValue()->Send(new ViewMsg_GetCacheResourceStats());
947}
948
949void MetricsService::OnMemoryDetailCollectionDone() {
[email protected]c9a3ef82009-05-28 22:02:46950 DCHECK(IsSingleThreaded());
[email protected]7f7f1962011-04-20 15:58:16951 // This function should only be called as the callback from an ansynchronous
952 // step.
953 DCHECK(waiting_for_asynchronus_reporting_step_);
[email protected]c9a3ef82009-05-28 22:02:46954
[email protected]7f7f1962011-04-20 15:58:16955 // Right before the UMA transmission gets started, there's one more thing we'd
956 // like to record: the histogram of memory usage, so we spawn a task to
957 // collect the memory details and when that task is finished, it will call
958 // OnMemoryDetailCollectionDone, which will call HistogramSynchronization to
959 // collect histograms from all renderers and then we will call
960 // OnHistogramSynchronizationDone to continue processing.
[email protected]c9a3ef82009-05-28 22:02:46961
962 // Create a callback_task for OnHistogramSynchronizationDone.
963 Task* callback_task = log_sender_factory_.NewRunnableMethod(
964 &MetricsService::OnHistogramSynchronizationDone);
965
966 // Set up the callback to task to call after we receive histograms from all
967 // renderer processes. Wait time specifies how long to wait before absolutely
968 // calling us back on the task.
969 HistogramSynchronizer::FetchRendererHistogramsAsynchronously(
970 MessageLoop::current(), callback_task,
971 kMaxHistogramGatheringWaitDuration);
972}
973
974void MetricsService::OnHistogramSynchronizationDone() {
initial.commit09911bf2008-07-26 23:55:29975 DCHECK(IsSingleThreaded());
976
[email protected]7f7f1962011-04-20 15:58:16977 // If somehow there is a fetch in progress, we return and hope things work
978 // out. The scheduler isn't informed since if this happens, the scheduler
979 // will get a response from the upload.
initial.commit09911bf2008-07-26 23:55:29980 DCHECK(!current_fetch_.get());
[email protected]7f7f1962011-04-20 15:58:16981 if (current_fetch_.get())
982 return;
983
984 // This function should only be called as the callback from an ansynchronous
985 // step.
986 DCHECK(waiting_for_asynchronus_reporting_step_);
987 waiting_for_asynchronus_reporting_step_ = false;
initial.commit09911bf2008-07-26 23:55:29988
[email protected]d01b8732008-10-16 02:18:07989 // If we're getting no notifications, then the log won't have much in it, and
990 // it's possible the computer is about to go to sleep, so don't upload and
[email protected]7f7f1962011-04-20 15:58:16991 // stop the scheduler.
992 // Similarly, if logs should no longer be uploaded, stop here.
993 if (idle_since_last_transmission_ ||
994 !recording_active() || !reporting_active()) {
995 scheduler_->Stop();
996 scheduler_->UploadCancelled();
[email protected]d01b8732008-10-16 02:18:07997 return;
998 }
999
[email protected]d01b8732008-10-16 02:18:071000 MakePendingLog();
initial.commit09911bf2008-07-26 23:55:291001
[email protected]d01b8732008-10-16 02:18:071002 // MakePendingLog should have put something in the pending log, if it didn't,
[email protected]7f7f1962011-04-20 15:58:161003 // we skip this upload and hope things work out next time.
[email protected]d01b8732008-10-16 02:18:071004 if (!pending_log()) {
[email protected]7f7f1962011-04-20 15:58:161005 scheduler_->UploadCancelled();
[email protected]d01b8732008-10-16 02:18:071006 return;
1007 }
initial.commit09911bf2008-07-26 23:55:291008
[email protected]d01b8732008-10-16 02:18:071009 PrepareFetchWithPendingLog();
1010
1011 if (!current_fetch_.get()) {
1012 // Compression failed, and log discarded :-/.
1013 DiscardPendingLog();
[email protected]7f7f1962011-04-20 15:58:161014 scheduler_->UploadCancelled();
[email protected]d01b8732008-10-16 02:18:071015 // TODO(jar): If compression failed, we should have created a tiny log and
1016 // compressed that, so that we can signal that we're losing logs.
1017 return;
1018 }
1019
[email protected]7f7f1962011-04-20 15:58:161020 DCHECK(!waiting_for_asynchronus_reporting_step_);
[email protected]d01b8732008-10-16 02:18:071021
[email protected]7f7f1962011-04-20 15:58:161022 waiting_for_asynchronus_reporting_step_ = true;
[email protected]d01b8732008-10-16 02:18:071023 current_fetch_->Start();
1024
1025 HandleIdleSinceLastTransmission(true);
1026}
1027
1028
1029void MetricsService::MakePendingLog() {
1030 if (pending_log())
1031 return;
1032
1033 switch (state_) {
1034 case INITIALIZED:
[email protected]85ed9d42010-06-08 22:37:441035 case INIT_TASK_SCHEDULED: // We should be further along by now.
[email protected]d01b8732008-10-16 02:18:071036 DCHECK(false);
1037 return;
1038
[email protected]85ed9d42010-06-08 22:37:441039 case INIT_TASK_DONE:
[email protected]d01b8732008-10-16 02:18:071040 // We need to wait for the initial log to be ready before sending
1041 // anything, because the server will tell us whether it wants to hear
1042 // from us.
1043 PrepareInitialLog();
[email protected]85ed9d42010-06-08 22:37:441044 DCHECK(state_ == INIT_TASK_DONE);
[email protected]d01b8732008-10-16 02:18:071045 RecallUnsentLogs();
1046 state_ = INITIAL_LOG_READY;
1047 break;
1048
1049 case SEND_OLD_INITIAL_LOGS:
[email protected]cac78842008-11-27 01:02:201050 if (!unsent_initial_logs_.empty()) {
[email protected]46f89e142010-07-19 08:00:421051 compressed_log_ = unsent_initial_logs_.back();
[email protected]cac78842008-11-27 01:02:201052 break;
1053 }
[email protected]d01b8732008-10-16 02:18:071054 state_ = SENDING_OLD_LOGS;
1055 // Fall through.
initial.commit09911bf2008-07-26 23:55:291056
[email protected]d01b8732008-10-16 02:18:071057 case SENDING_OLD_LOGS:
1058 if (!unsent_ongoing_logs_.empty()) {
[email protected]46f89e142010-07-19 08:00:421059 compressed_log_ = unsent_ongoing_logs_.back();
[email protected]d01b8732008-10-16 02:18:071060 break;
1061 }
1062 state_ = SENDING_CURRENT_LOGS;
1063 // Fall through.
1064
1065 case SENDING_CURRENT_LOGS:
1066 StopRecording(&pending_log_);
1067 StartRecording();
1068 break;
1069
1070 default:
[email protected]a063c102010-07-22 22:20:191071 NOTREACHED();
[email protected]d01b8732008-10-16 02:18:071072 return;
1073 }
1074
1075 DCHECK(pending_log());
1076}
1077
initial.commit09911bf2008-07-26 23:55:291078void MetricsService::PrepareInitialLog() {
[email protected]85ed9d42010-06-08 22:37:441079 DCHECK(state_ == INIT_TASK_DONE);
initial.commit09911bf2008-07-26 23:55:291080
1081 MetricsLog* log = new MetricsLog(client_id_, session_id_);
[email protected]85ed9d42010-06-08 22:37:441082 log->set_hardware_class(hardware_class_); // Adds to initial log.
[email protected]35fa6a22009-08-15 00:04:011083 log->RecordEnvironment(plugins_, profile_dictionary_.get());
initial.commit09911bf2008-07-26 23:55:291084
1085 // Histograms only get written to current_log_, so setup for the write.
[email protected]1226abb2010-06-10 18:01:281086 MetricsLogBase* save_log = current_log_;
initial.commit09911bf2008-07-26 23:55:291087 current_log_ = log;
1088 RecordCurrentHistograms(); // Into current_log_... which is really log.
1089 current_log_ = save_log;
1090
1091 log->CloseLog();
1092 DCHECK(!pending_log());
1093 pending_log_ = log;
1094}
1095
[email protected]46f89e142010-07-19 08:00:421096// static
1097MetricsService::LogRecallStatus MetricsService::RecallUnsentLogsHelper(
1098 const ListValue& list,
1099 std::vector<std::string>* local_list) {
1100 DCHECK(local_list->empty());
1101 if (list.GetSize() == 0)
1102 return MakeRecallStatusHistogram(LIST_EMPTY);
1103 if (list.GetSize() < 3)
1104 return MakeRecallStatusHistogram(LIST_SIZE_TOO_SMALL);
initial.commit09911bf2008-07-26 23:55:291105
[email protected]46f89e142010-07-19 08:00:421106 // The size is stored at the beginning of the list.
1107 int size;
1108 bool valid = (*list.begin())->GetAsInteger(&size);
1109 if (!valid)
1110 return MakeRecallStatusHistogram(LIST_SIZE_MISSING);
1111
1112 // Account for checksum and size included in the list.
1113 if (static_cast<unsigned int>(size) !=
1114 list.GetSize() - kChecksumEntryCount)
1115 return MakeRecallStatusHistogram(LIST_SIZE_CORRUPTION);
1116
1117 MD5Context ctx;
1118 MD5Init(&ctx);
1119 std::string encoded_log;
1120 std::string decoded_log;
1121 for (ListValue::const_iterator it = list.begin() + 1;
1122 it != list.end() - 1; ++it) { // Last element is the checksum.
1123 valid = (*it)->GetAsString(&encoded_log);
1124 if (!valid) {
1125 local_list->clear();
1126 return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION);
1127 }
1128
1129 MD5Update(&ctx, encoded_log.data(), encoded_log.length());
1130
1131 if (!base::Base64Decode(encoded_log, &decoded_log)) {
1132 local_list->clear();
1133 return MakeRecallStatusHistogram(DECODE_FAIL);
1134 }
1135 local_list->push_back(decoded_log);
1136 }
1137
1138 // Verify checksum.
1139 MD5Digest digest;
1140 MD5Final(&digest, &ctx);
1141 std::string recovered_md5;
1142 // We store the hash at the end of the list.
1143 valid = (*(list.end() - 1))->GetAsString(&recovered_md5);
1144 if (!valid) {
1145 local_list->clear();
1146 return MakeRecallStatusHistogram(CHECKSUM_STRING_CORRUPTION);
1147 }
1148 if (recovered_md5 != MD5DigestToBase16(digest)) {
1149 local_list->clear();
1150 return MakeRecallStatusHistogram(CHECKSUM_CORRUPTION);
1151 }
1152 return MakeRecallStatusHistogram(RECALL_SUCCESS);
1153}
1154void MetricsService::RecallUnsentLogs() {
initial.commit09911bf2008-07-26 23:55:291155 PrefService* local_state = g_browser_process->local_state();
1156 DCHECK(local_state);
1157
[email protected]f8628c22011-04-05 12:10:181158 const ListValue* unsent_initial_logs = local_state->GetList(
initial.commit09911bf2008-07-26 23:55:291159 prefs::kMetricsInitialLogs);
[email protected]46f89e142010-07-19 08:00:421160 RecallUnsentLogsHelper(*unsent_initial_logs, &unsent_initial_logs_);
initial.commit09911bf2008-07-26 23:55:291161
[email protected]f8628c22011-04-05 12:10:181162 const ListValue* unsent_ongoing_logs = local_state->GetList(
initial.commit09911bf2008-07-26 23:55:291163 prefs::kMetricsOngoingLogs);
[email protected]46f89e142010-07-19 08:00:421164 RecallUnsentLogsHelper(*unsent_ongoing_logs, &unsent_ongoing_logs_);
1165}
1166
1167// static
1168void MetricsService::StoreUnsentLogsHelper(
1169 const std::vector<std::string>& local_list,
1170 const size_t kMaxLocalListSize,
1171 ListValue* list) {
1172 list->Clear();
1173 size_t start = 0;
1174 if (local_list.size() > kMaxLocalListSize)
1175 start = local_list.size() - kMaxLocalListSize;
1176 DCHECK(start <= local_list.size());
1177 if (local_list.size() == start)
1178 return;
1179
1180 // Store size at the beginning of the list.
1181 list->Append(Value::CreateIntegerValue(local_list.size() - start));
1182
1183 MD5Context ctx;
1184 MD5Init(&ctx);
1185 std::string encoded_log;
1186 for (std::vector<std::string>::const_iterator it = local_list.begin() + start;
1187 it != local_list.end(); ++it) {
1188 // We encode the compressed log as Value::CreateStringValue() expects to
1189 // take a valid UTF8 string.
1190 if (!base::Base64Encode(*it, &encoded_log)) {
1191 MakeStoreStatusHistogram(ENCODE_FAIL);
1192 list->Clear();
1193 return;
1194 }
1195 MD5Update(&ctx, encoded_log.data(), encoded_log.length());
1196 list->Append(Value::CreateStringValue(encoded_log));
initial.commit09911bf2008-07-26 23:55:291197 }
[email protected]46f89e142010-07-19 08:00:421198
1199 // Append hash to the end of the list.
1200 MD5Digest digest;
1201 MD5Final(&digest, &ctx);
1202 list->Append(Value::CreateStringValue(MD5DigestToBase16(digest)));
1203 DCHECK(list->GetSize() >= 3); // Minimum of 3 elements (size, data, hash).
[email protected]4e95d202010-07-24 01:47:561204 MakeStoreStatusHistogram(STORE_SUCCESS);
initial.commit09911bf2008-07-26 23:55:291205}
1206
1207void MetricsService::StoreUnsentLogs() {
1208 if (state_ < INITIAL_LOG_READY)
1209 return; // We never Recalled the prior unsent logs.
1210
1211 PrefService* local_state = g_browser_process->local_state();
1212 DCHECK(local_state);
1213
[email protected]f8628c22011-04-05 12:10:181214 {
1215 ListPrefUpdate update(local_state, prefs::kMetricsInitialLogs);
1216 ListValue* unsent_initial_logs = update.Get();
1217 StoreUnsentLogsHelper(unsent_initial_logs_, kMaxInitialLogsPersisted,
1218 unsent_initial_logs);
1219 }
initial.commit09911bf2008-07-26 23:55:291220
[email protected]f8628c22011-04-05 12:10:181221 {
1222 ListPrefUpdate update(local_state, prefs::kMetricsOngoingLogs);
1223 ListValue* unsent_ongoing_logs = update.Get();
1224 StoreUnsentLogsHelper(unsent_ongoing_logs_, kMaxOngoingLogsPersisted,
1225 unsent_ongoing_logs);
1226 }
initial.commit09911bf2008-07-26 23:55:291227}
1228
1229void MetricsService::PreparePendingLogText() {
1230 DCHECK(pending_log());
[email protected]46f89e142010-07-19 08:00:421231 if (!compressed_log_.empty())
initial.commit09911bf2008-07-26 23:55:291232 return;
[email protected]9ffcccf42009-09-15 22:19:181233 int text_size = pending_log_->GetEncodedLogSize();
1234
[email protected]46f89e142010-07-19 08:00:421235 std::string pending_log_text;
1236 // Leave room for the NULL terminator.
1237 pending_log_->GetEncodedLog(WriteInto(&pending_log_text, text_size + 1),
[email protected]9ffcccf42009-09-15 22:19:181238 text_size);
[email protected]46f89e142010-07-19 08:00:421239
1240 if (Bzip2Compress(pending_log_text, &compressed_log_)) {
1241 // Allow security conscious users to see all metrics logs that we send.
[email protected]666205032010-10-21 20:56:581242 VLOG(1) << "COMPRESSED FOLLOWING METRICS LOG: " << pending_log_text;
[email protected]46f89e142010-07-19 08:00:421243 } else {
1244 LOG(DFATAL) << "Failed to compress log for transmission.";
1245 // We can't discard the logs as other caller functions expect that
1246 // |compressed_log_| not be empty. We can detect this failure at the server
1247 // after we transmit.
1248 compressed_log_ = "Unable to compress!";
1249 MakeStoreStatusHistogram(COMPRESS_FAIL);
1250 return;
1251 }
initial.commit09911bf2008-07-26 23:55:291252}
1253
[email protected]d01b8732008-10-16 02:18:071254void MetricsService::PrepareFetchWithPendingLog() {
initial.commit09911bf2008-07-26 23:55:291255 DCHECK(pending_log());
1256 DCHECK(!current_fetch_.get());
1257 PreparePendingLogText();
[email protected]46f89e142010-07-19 08:00:421258 DCHECK(!compressed_log_.empty());
[email protected]cac78842008-11-27 01:02:201259
[email protected]79bf0b72009-04-27 21:30:551260 current_fetch_.reset(new URLFetcher(GURL(WideToUTF16(server_url_)),
1261 URLFetcher::POST,
initial.commit09911bf2008-07-26 23:55:291262 this));
1263 current_fetch_->set_request_context(Profile::GetDefaultRequestContext());
[email protected]46f89e142010-07-19 08:00:421264 current_fetch_->set_upload_data(kMetricsType, compressed_log_);
initial.commit09911bf2008-07-26 23:55:291265}
1266
[email protected]f90bf0d92011-01-13 02:12:441267static const char* StatusToString(const net::URLRequestStatus& status) {
initial.commit09911bf2008-07-26 23:55:291268 switch (status.status()) {
[email protected]f90bf0d92011-01-13 02:12:441269 case net::URLRequestStatus::SUCCESS:
initial.commit09911bf2008-07-26 23:55:291270 return "SUCCESS";
1271
[email protected]f90bf0d92011-01-13 02:12:441272 case net::URLRequestStatus::IO_PENDING:
initial.commit09911bf2008-07-26 23:55:291273 return "IO_PENDING";
1274
[email protected]f90bf0d92011-01-13 02:12:441275 case net::URLRequestStatus::HANDLED_EXTERNALLY:
initial.commit09911bf2008-07-26 23:55:291276 return "HANDLED_EXTERNALLY";
1277
[email protected]f90bf0d92011-01-13 02:12:441278 case net::URLRequestStatus::CANCELED:
initial.commit09911bf2008-07-26 23:55:291279 return "CANCELED";
1280
[email protected]f90bf0d92011-01-13 02:12:441281 case net::URLRequestStatus::FAILED:
initial.commit09911bf2008-07-26 23:55:291282 return "FAILED";
1283
1284 default:
[email protected]a063c102010-07-22 22:20:191285 NOTREACHED();
initial.commit09911bf2008-07-26 23:55:291286 return "Unknown";
1287 }
1288}
1289
1290void MetricsService::OnURLFetchComplete(const URLFetcher* source,
1291 const GURL& url,
[email protected]f90bf0d92011-01-13 02:12:441292 const net::URLRequestStatus& status,
initial.commit09911bf2008-07-26 23:55:291293 int response_code,
[email protected]cb04f5e2011-05-06 01:10:001294 const net::ResponseCookies& cookies,
initial.commit09911bf2008-07-26 23:55:291295 const std::string& data) {
[email protected]7f7f1962011-04-20 15:58:161296 DCHECK(waiting_for_asynchronus_reporting_step_);
1297 waiting_for_asynchronus_reporting_step_ = false;
initial.commit09911bf2008-07-26 23:55:291298 DCHECK(current_fetch_.get());
1299 current_fetch_.reset(NULL); // We're not allowed to re-use it.
1300
1301 // Confirm send so that we can move on.
[email protected]666205032010-10-21 20:56:581302 VLOG(1) << "METRICS RESPONSE CODE: " << response_code
1303 << " status=" << StatusToString(status);
[email protected]252873ef2008-08-04 21:59:451304
[email protected]7f7f1962011-04-20 15:58:161305 bool upload_succeeded = response_code == 200;
1306
[email protected]0eb34fee2009-01-21 08:04:381307 // Provide boolean for error recovery (allow us to ignore response_code).
[email protected]dc6f4962009-02-13 01:25:501308 bool discard_log = false;
[email protected]0eb34fee2009-01-21 08:04:381309
[email protected]7f7f1962011-04-20 15:58:161310 if (!upload_succeeded &&
[email protected]46f89e142010-07-19 08:00:421311 (compressed_log_.length() >
1312 static_cast<size_t>(kUploadLogAvoidRetransmitSize))) {
[email protected]553dba62009-02-24 19:08:231313 UMA_HISTOGRAM_COUNTS("UMA.Large Rejected Log was Discarded",
[email protected]46f89e142010-07-19 08:00:421314 static_cast<int>(compressed_log_.length()));
[email protected]0eb34fee2009-01-21 08:04:381315 discard_log = true;
1316 } else if (response_code == 400) {
1317 // Bad syntax. Retransmission won't work.
[email protected]553dba62009-02-24 19:08:231318 UMA_HISTOGRAM_COUNTS("UMA.Unacceptable_Log_Discarded", state_);
[email protected]0eb34fee2009-01-21 08:04:381319 discard_log = true;
[email protected]68475e602008-08-22 03:21:151320 }
1321
[email protected]7f7f1962011-04-20 15:58:161322 if (!upload_succeeded && !discard_log) {
[email protected]666205032010-10-21 20:56:581323 VLOG(1) << "METRICS: transmission attempt returned a failure code: "
1324 << response_code << ". Verify network connectivity";
[email protected]7f7f1962011-04-20 15:58:161325 LogBadResponseCode();
[email protected]0eb34fee2009-01-21 08:04:381326 } else { // Successful receipt (or we are discarding log).
[email protected]666205032010-10-21 20:56:581327 VLOG(1) << "METRICS RESPONSE DATA: " << data;
initial.commit09911bf2008-07-26 23:55:291328 switch (state_) {
1329 case INITIAL_LOG_READY:
1330 state_ = SEND_OLD_INITIAL_LOGS;
1331 break;
1332
1333 case SEND_OLD_INITIAL_LOGS:
1334 DCHECK(!unsent_initial_logs_.empty());
1335 unsent_initial_logs_.pop_back();
1336 StoreUnsentLogs();
1337 break;
1338
1339 case SENDING_OLD_LOGS:
1340 DCHECK(!unsent_ongoing_logs_.empty());
1341 unsent_ongoing_logs_.pop_back();
1342 StoreUnsentLogs();
1343 break;
1344
1345 case SENDING_CURRENT_LOGS:
1346 break;
1347
1348 default:
[email protected]a063c102010-07-22 22:20:191349 NOTREACHED();
initial.commit09911bf2008-07-26 23:55:291350 break;
1351 }
[email protected]d01b8732008-10-16 02:18:071352
initial.commit09911bf2008-07-26 23:55:291353 DiscardPendingLog();
[email protected]29be92552008-08-07 22:49:271354 // Since we sent a log, make sure our in-memory state is recorded to disk.
1355 PrefService* local_state = g_browser_process->local_state();
1356 DCHECK(local_state);
1357 if (local_state)
[email protected]6faa0e0d2009-04-28 06:50:361358 local_state->ScheduleSavePersistentPrefs();
[email protected]252873ef2008-08-04 21:59:451359
[email protected]7f7f1962011-04-20 15:58:161360 if (unsent_logs())
initial.commit09911bf2008-07-26 23:55:291361 DCHECK(state_ < SENDING_CURRENT_LOGS);
initial.commit09911bf2008-07-26 23:55:291362 }
[email protected]252873ef2008-08-04 21:59:451363
[email protected]7f7f1962011-04-20 15:58:161364 // Error 400 indicates a problem with the log, not with the server, so
1365 // don't consider that a sign that the server is in trouble.
1366 bool server_is_healthy = upload_succeeded || response_code == 400;
1367
1368 scheduler_->UploadFinished(server_is_healthy, unsent_logs());
initial.commit09911bf2008-07-26 23:55:291369}
1370
[email protected]7f7f1962011-04-20 15:58:161371void MetricsService::LogBadResponseCode() {
[email protected]666205032010-10-21 20:56:581372 VLOG(1) << "Verify your metrics logs are formatted correctly. Verify server "
1373 "is active at " << server_url_;
[email protected]252873ef2008-08-04 21:59:451374 if (!pending_log()) {
[email protected]666205032010-10-21 20:56:581375 VLOG(1) << "METRICS: Recorder shutdown during log transmission.";
[email protected]252873ef2008-08-04 21:59:451376 } else {
[email protected]7f7f1962011-04-20 15:58:161377 VLOG(1) << "METRICS: transmission retry being scheduled for "
[email protected]666205032010-10-21 20:56:581378 << compressed_log_;
initial.commit09911bf2008-07-26 23:55:291379 }
initial.commit09911bf2008-07-26 23:55:291380}
1381
initial.commit09911bf2008-07-26 23:55:291382void MetricsService::LogWindowChange(NotificationType type,
1383 const NotificationSource& source,
1384 const NotificationDetails& details) {
[email protected]534e54b2008-08-13 15:40:091385 int controller_id = -1;
1386 uintptr_t window_or_tab = source.map_key();
initial.commit09911bf2008-07-26 23:55:291387 MetricsLog::WindowEventType window_type;
1388
1389 // Note: since we stop all logging when a single OTR session is active, it is
1390 // possible that we start getting notifications about a window that we don't
1391 // know about.
[email protected]534e54b2008-08-13 15:40:091392 if (window_map_.find(window_or_tab) == window_map_.end()) {
1393 controller_id = next_window_id_++;
1394 window_map_[window_or_tab] = controller_id;
initial.commit09911bf2008-07-26 23:55:291395 } else {
[email protected]534e54b2008-08-13 15:40:091396 controller_id = window_map_[window_or_tab];
initial.commit09911bf2008-07-26 23:55:291397 }
[email protected]92745242009-06-12 16:52:211398 DCHECK_NE(controller_id, -1);
initial.commit09911bf2008-07-26 23:55:291399
[email protected]bfd04a62009-02-01 18:16:561400 switch (type.value) {
1401 case NotificationType::TAB_PARENTED:
1402 case NotificationType::BROWSER_OPENED:
initial.commit09911bf2008-07-26 23:55:291403 window_type = MetricsLog::WINDOW_CREATE;
1404 break;
1405
[email protected]bfd04a62009-02-01 18:16:561406 case NotificationType::TAB_CLOSING:
1407 case NotificationType::BROWSER_CLOSED:
[email protected]534e54b2008-08-13 15:40:091408 window_map_.erase(window_map_.find(window_or_tab));
initial.commit09911bf2008-07-26 23:55:291409 window_type = MetricsLog::WINDOW_DESTROY;
1410 break;
1411
1412 default:
[email protected]a063c102010-07-22 22:20:191413 NOTREACHED();
[email protected]68d74f02009-02-13 01:36:501414 return;
initial.commit09911bf2008-07-26 23:55:291415 }
1416
[email protected]534e54b2008-08-13 15:40:091417 // TODO(brettw) we should have some kind of ID for the parent.
1418 current_log_->RecordWindowEvent(window_type, controller_id, 0);
initial.commit09911bf2008-07-26 23:55:291419}
1420
1421void MetricsService::LogLoadComplete(NotificationType type,
1422 const NotificationSource& source,
1423 const NotificationDetails& details) {
1424 if (details == NotificationService::NoDetails())
1425 return;
1426
[email protected]68475e602008-08-22 03:21:151427 // TODO(jar): There is a bug causing this to be called too many times, and
1428 // the log overflows. For now, we won't record these events.
[email protected]553dba62009-02-24 19:08:231429 UMA_HISTOGRAM_COUNTS("UMA.LogLoadComplete called", 1);
[email protected]68475e602008-08-22 03:21:151430 return;
1431
initial.commit09911bf2008-07-26 23:55:291432 const Details<LoadNotificationDetails> load_details(details);
[email protected]534e54b2008-08-13 15:40:091433 int controller_id = window_map_[details.map_key()];
1434 current_log_->RecordLoadEvent(controller_id,
initial.commit09911bf2008-07-26 23:55:291435 load_details->url(),
1436 load_details->origin(),
1437 load_details->session_index(),
1438 load_details->load_time());
1439}
1440
[email protected]57ecc4b2010-08-11 03:02:511441void MetricsService::IncrementPrefValue(const char* path) {
[email protected]e73c01972008-08-13 00:18:241442 PrefService* pref = g_browser_process->local_state();
1443 DCHECK(pref);
1444 int value = pref->GetInteger(path);
1445 pref->SetInteger(path, value + 1);
1446}
1447
[email protected]57ecc4b2010-08-11 03:02:511448void MetricsService::IncrementLongPrefsValue(const char* path) {
[email protected]0bb1a622009-03-04 03:22:321449 PrefService* pref = g_browser_process->local_state();
1450 DCHECK(pref);
1451 int64 value = pref->GetInt64(path);
[email protected]b42c5e42010-06-03 20:43:251452 pref->SetInt64(path, value + 1);
[email protected]0bb1a622009-03-04 03:22:321453}
1454
initial.commit09911bf2008-07-26 23:55:291455void MetricsService::LogLoadStarted() {
[email protected]e73c01972008-08-13 00:18:241456 IncrementPrefValue(prefs::kStabilityPageLoadCount);
[email protected]0bb1a622009-03-04 03:22:321457 IncrementLongPrefsValue(prefs::kUninstallMetricsPageLoadCount);
[email protected]0b33f80b2008-12-17 21:34:361458 // We need to save the prefs, as page load count is a critical stat, and it
1459 // might be lost due to a crash :-(.
initial.commit09911bf2008-07-26 23:55:291460}
1461
initial.commit09911bf2008-07-26 23:55:291462void MetricsService::LogRendererCrash() {
[email protected]e73c01972008-08-13 00:18:241463 IncrementPrefValue(prefs::kStabilityRendererCrashCount);
initial.commit09911bf2008-07-26 23:55:291464}
1465
[email protected]1f085622009-12-04 05:33:451466void MetricsService::LogExtensionRendererCrash() {
1467 IncrementPrefValue(prefs::kStabilityExtensionRendererCrashCount);
1468}
1469
initial.commit09911bf2008-07-26 23:55:291470void MetricsService::LogRendererHang() {
[email protected]e73c01972008-08-13 00:18:241471 IncrementPrefValue(prefs::kStabilityRendererHangCount);
initial.commit09911bf2008-07-26 23:55:291472}
1473
[email protected]466f3c12011-03-23 21:20:381474void MetricsService::LogCleanShutdown() {
1475 RecordBooleanPrefValue(prefs::kStabilityExitedCleanly, true);
1476}
1477
[email protected]c1834a92011-01-21 18:21:031478#if defined(OS_CHROMEOS)
1479void MetricsService::LogChromeOSCrash(const std::string &crash_type) {
1480 if (crash_type == "user")
1481 IncrementPrefValue(prefs::kStabilityOtherUserCrashCount);
1482 else if (crash_type == "kernel")
1483 IncrementPrefValue(prefs::kStabilityKernelCrashCount);
1484 else if (crash_type == "uncleanshutdown")
1485 IncrementPrefValue(prefs::kStabilitySystemUncleanShutdownCount);
1486 else
1487 NOTREACHED() << "Unexpected Chrome OS crash type " << crash_type;
1488 // Wake up metrics logs sending if necessary now that new
1489 // log data is available.
1490 HandleIdleSinceLastTransmission(false);
1491}
1492#endif // OS_CHROMEOS
1493
[email protected]a27a9382009-02-11 23:55:101494void MetricsService::LogChildProcessChange(
1495 NotificationType type,
1496 const NotificationSource& source,
1497 const NotificationDetails& details) {
[email protected]0d84c5d2009-10-09 01:10:421498 Details<ChildProcessInfo> child_details(details);
1499 const std::wstring& child_name = child_details->name();
1500
[email protected]a27a9382009-02-11 23:55:101501 if (child_process_stats_buffer_.find(child_name) ==
1502 child_process_stats_buffer_.end()) {
[email protected]0d84c5d2009-10-09 01:10:421503 child_process_stats_buffer_[child_name] =
1504 ChildProcessStats(child_details->type());
initial.commit09911bf2008-07-26 23:55:291505 }
1506
[email protected]a27a9382009-02-11 23:55:101507 ChildProcessStats& stats = child_process_stats_buffer_[child_name];
[email protected]bfd04a62009-02-01 18:16:561508 switch (type.value) {
[email protected]a27a9382009-02-11 23:55:101509 case NotificationType::CHILD_PROCESS_HOST_CONNECTED:
initial.commit09911bf2008-07-26 23:55:291510 stats.process_launches++;
1511 break;
1512
[email protected]a27a9382009-02-11 23:55:101513 case NotificationType::CHILD_INSTANCE_CREATED:
initial.commit09911bf2008-07-26 23:55:291514 stats.instances++;
1515 break;
1516
[email protected]a27a9382009-02-11 23:55:101517 case NotificationType::CHILD_PROCESS_CRASHED:
initial.commit09911bf2008-07-26 23:55:291518 stats.process_crashes++;
[email protected]1f085622009-12-04 05:33:451519 // Exclude plugin crashes from the count below because we report them via
1520 // a separate UMA metric.
1521 if (child_details->type() != ChildProcessInfo::PLUGIN_PROCESS) {
1522 IncrementPrefValue(prefs::kStabilityChildProcessCrashCount);
1523 }
initial.commit09911bf2008-07-26 23:55:291524 break;
1525
1526 default:
[email protected]a063c102010-07-22 22:20:191527 NOTREACHED() << "Unexpected notification type " << type.value;
initial.commit09911bf2008-07-26 23:55:291528 return;
1529 }
1530}
1531
1532// Recursively counts the number of bookmarks and folders in node.
[email protected]b3c33d462009-06-26 22:29:201533static void CountBookmarks(const BookmarkNode* node,
1534 int* bookmarks,
1535 int* folders) {
[email protected]037db002009-10-19 20:06:081536 if (node->type() == BookmarkNode::URL)
initial.commit09911bf2008-07-26 23:55:291537 (*bookmarks)++;
1538 else
1539 (*folders)++;
[email protected]9c1a75a2011-03-10 02:38:121540 for (int i = 0; i < node->child_count(); ++i)
initial.commit09911bf2008-07-26 23:55:291541 CountBookmarks(node->GetChild(i), bookmarks, folders);
1542}
1543
[email protected]b3c33d462009-06-26 22:29:201544void MetricsService::LogBookmarks(const BookmarkNode* node,
[email protected]57ecc4b2010-08-11 03:02:511545 const char* num_bookmarks_key,
1546 const char* num_folders_key) {
initial.commit09911bf2008-07-26 23:55:291547 DCHECK(node);
1548 int num_bookmarks = 0;
1549 int num_folders = 0;
1550 CountBookmarks(node, &num_bookmarks, &num_folders);
1551 num_folders--; // Don't include the root folder in the count.
1552
1553 PrefService* pref = g_browser_process->local_state();
1554 DCHECK(pref);
1555 pref->SetInteger(num_bookmarks_key, num_bookmarks);
1556 pref->SetInteger(num_folders_key, num_folders);
1557}
1558
[email protected]d8e41ed2008-09-11 15:22:321559void MetricsService::LogBookmarks(BookmarkModel* model) {
initial.commit09911bf2008-07-26 23:55:291560 DCHECK(model);
1561 LogBookmarks(model->GetBookmarkBarNode(),
1562 prefs::kNumBookmarksOnBookmarkBar,
1563 prefs::kNumFoldersOnBookmarkBar);
1564 LogBookmarks(model->other_node(),
1565 prefs::kNumBookmarksInOtherBookmarkFolder,
1566 prefs::kNumFoldersInOtherBookmarkFolder);
1567 ScheduleNextStateSave();
1568}
1569
1570void MetricsService::LogKeywords(const TemplateURLModel* url_model) {
1571 DCHECK(url_model);
1572
1573 PrefService* pref = g_browser_process->local_state();
1574 DCHECK(pref);
1575 pref->SetInteger(prefs::kNumKeywords,
1576 static_cast<int>(url_model->GetTemplateURLs().size()));
1577 ScheduleNextStateSave();
1578}
1579
1580void MetricsService::RecordPluginChanges(PrefService* pref) {
[email protected]f8628c22011-04-05 12:10:181581 ListPrefUpdate update(pref, prefs::kStabilityPluginStats);
1582 ListValue* plugins = update.Get();
initial.commit09911bf2008-07-26 23:55:291583 DCHECK(plugins);
1584
1585 for (ListValue::iterator value_iter = plugins->begin();
1586 value_iter != plugins->end(); ++value_iter) {
1587 if (!(*value_iter)->IsType(Value::TYPE_DICTIONARY)) {
[email protected]a063c102010-07-22 22:20:191588 NOTREACHED();
initial.commit09911bf2008-07-26 23:55:291589 continue;
1590 }
1591
1592 DictionaryValue* plugin_dict = static_cast<DictionaryValue*>(*value_iter);
[email protected]57ecc4b2010-08-11 03:02:511593 std::string plugin_name;
[email protected]8e50b602009-03-03 22:59:431594 plugin_dict->GetString(prefs::kStabilityPluginName, &plugin_name);
[email protected]6470ee8f2009-03-03 20:46:401595 if (plugin_name.empty()) {
[email protected]a063c102010-07-22 22:20:191596 NOTREACHED();
initial.commit09911bf2008-07-26 23:55:291597 continue;
1598 }
1599
[email protected]57ecc4b2010-08-11 03:02:511600 // TODO(viettrungluu): remove conversions
1601 if (child_process_stats_buffer_.find(UTF8ToWide(plugin_name)) ==
[email protected]a27a9382009-02-11 23:55:101602 child_process_stats_buffer_.end())
initial.commit09911bf2008-07-26 23:55:291603 continue;
1604
[email protected]57ecc4b2010-08-11 03:02:511605 ChildProcessStats stats =
1606 child_process_stats_buffer_[UTF8ToWide(plugin_name)];
initial.commit09911bf2008-07-26 23:55:291607 if (stats.process_launches) {
1608 int launches = 0;
[email protected]8e50b602009-03-03 22:59:431609 plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches);
initial.commit09911bf2008-07-26 23:55:291610 launches += stats.process_launches;
[email protected]8e50b602009-03-03 22:59:431611 plugin_dict->SetInteger(prefs::kStabilityPluginLaunches, launches);
initial.commit09911bf2008-07-26 23:55:291612 }
1613 if (stats.process_crashes) {
1614 int crashes = 0;
[email protected]8e50b602009-03-03 22:59:431615 plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes);
initial.commit09911bf2008-07-26 23:55:291616 crashes += stats.process_crashes;
[email protected]8e50b602009-03-03 22:59:431617 plugin_dict->SetInteger(prefs::kStabilityPluginCrashes, crashes);
initial.commit09911bf2008-07-26 23:55:291618 }
1619 if (stats.instances) {
1620 int instances = 0;
[email protected]8e50b602009-03-03 22:59:431621 plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances);
initial.commit09911bf2008-07-26 23:55:291622 instances += stats.instances;
[email protected]8e50b602009-03-03 22:59:431623 plugin_dict->SetInteger(prefs::kStabilityPluginInstances, instances);
initial.commit09911bf2008-07-26 23:55:291624 }
1625
[email protected]57ecc4b2010-08-11 03:02:511626 child_process_stats_buffer_.erase(UTF8ToWide(plugin_name));
initial.commit09911bf2008-07-26 23:55:291627 }
1628
1629 // Now go through and add dictionaries for plugins that didn't already have
1630 // reports in Local State.
[email protected]a27a9382009-02-11 23:55:101631 for (std::map<std::wstring, ChildProcessStats>::iterator cache_iter =
1632 child_process_stats_buffer_.begin();
1633 cache_iter != child_process_stats_buffer_.end(); ++cache_iter) {
[email protected]a27a9382009-02-11 23:55:101634 ChildProcessStats stats = cache_iter->second;
[email protected]0d84c5d2009-10-09 01:10:421635
1636 // Insert only plugins information into the plugins list.
1637 if (ChildProcessInfo::PLUGIN_PROCESS != stats.process_type)
1638 continue;
1639
[email protected]57ecc4b2010-08-11 03:02:511640 // TODO(viettrungluu): remove conversion
1641 std::string plugin_name = WideToUTF8(cache_iter->first);
[email protected]0d84c5d2009-10-09 01:10:421642
initial.commit09911bf2008-07-26 23:55:291643 DictionaryValue* plugin_dict = new DictionaryValue;
1644
[email protected]8e50b602009-03-03 22:59:431645 plugin_dict->SetString(prefs::kStabilityPluginName, plugin_name);
1646 plugin_dict->SetInteger(prefs::kStabilityPluginLaunches,
initial.commit09911bf2008-07-26 23:55:291647 stats.process_launches);
[email protected]8e50b602009-03-03 22:59:431648 plugin_dict->SetInteger(prefs::kStabilityPluginCrashes,
initial.commit09911bf2008-07-26 23:55:291649 stats.process_crashes);
[email protected]8e50b602009-03-03 22:59:431650 plugin_dict->SetInteger(prefs::kStabilityPluginInstances,
initial.commit09911bf2008-07-26 23:55:291651 stats.instances);
1652 plugins->Append(plugin_dict);
1653 }
[email protected]a27a9382009-02-11 23:55:101654 child_process_stats_buffer_.clear();
initial.commit09911bf2008-07-26 23:55:291655}
1656
1657bool MetricsService::CanLogNotification(NotificationType type,
1658 const NotificationSource& source,
1659 const NotificationDetails& details) {
[email protected]2c910b72011-03-08 21:16:321660 // We simply don't log anything to UMA if there is a single incognito
initial.commit09911bf2008-07-26 23:55:291661 // session visible. The problem is that we always notify using the orginal
1662 // profile in order to simplify notification processing.
1663 return !BrowserList::IsOffTheRecordSessionActive();
1664}
1665
[email protected]57ecc4b2010-08-11 03:02:511666void MetricsService::RecordBooleanPrefValue(const char* path, bool value) {
initial.commit09911bf2008-07-26 23:55:291667 DCHECK(IsSingleThreaded());
1668
1669 PrefService* pref = g_browser_process->local_state();
1670 DCHECK(pref);
1671
1672 pref->SetBoolean(path, value);
1673 RecordCurrentState(pref);
1674}
1675
1676void MetricsService::RecordCurrentState(PrefService* pref) {
[email protected]0bb1a622009-03-04 03:22:321677 pref->SetInt64(prefs::kStabilityLastTimestampSec, Time::Now().ToTimeT());
initial.commit09911bf2008-07-26 23:55:291678
1679 RecordPluginChanges(pref);
1680}
1681
initial.commit09911bf2008-07-26 23:55:291682static bool IsSingleThreaded() {
[email protected]ce072a72010-12-31 20:02:161683 static base::PlatformThreadId thread_id = 0;
initial.commit09911bf2008-07-26 23:55:291684 if (!thread_id)
[email protected]ce072a72010-12-31 20:02:161685 thread_id = base::PlatformThread::CurrentId();
1686 return base::PlatformThread::CurrentId() == thread_id;
initial.commit09911bf2008-07-26 23:55:291687}
[email protected]5ccaa412009-11-13 22:00:161688
1689#if defined(OS_CHROMEOS)
[email protected]29cf16772010-04-21 15:13:471690void MetricsService::StartExternalMetrics() {
[email protected]5ccaa412009-11-13 22:00:161691 external_metrics_ = new chromeos::ExternalMetrics;
[email protected]29cf16772010-04-21 15:13:471692 external_metrics_->Start();
[email protected]5ccaa412009-11-13 22:00:161693}
1694#endif