blob: 2c5ea8a00ef5556f45a2068a6e8332e6a60cb677 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file defines a service that collects information about the user
// experience in order to help improve future versions of the app.
#ifndef CHROME_BROWSER_METRICS_METRICS_SERVICE_H_
#define CHROME_BROWSER_METRICS_METRICS_SERVICE_H_
#include <map>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/process/kill.h"
#include "base/time/time.h"
#include "chrome/browser/metrics/metrics_log.h"
#include "chrome/browser/metrics/tracking_synchronizer_observer.h"
#include "chrome/common/metrics/metrics_service_base.h"
#include "chrome/installer/util/google_update_settings.h"
#include "content/public/browser/browser_child_process_observer.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/user_metrics.h"
#include "net/url_request/url_fetcher_delegate.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/external_metrics.h"
#endif
class MetricsReportingScheduler;
class PrefService;
class PrefRegistrySimple;
class Profile;
class TemplateURLService;
namespace base {
class DictionaryValue;
class MessageLoopProxy;
}
namespace chrome_variations {
struct ActiveGroupId;
}
namespace content {
class RenderProcessHost;
class WebContents;
struct WebPluginInfo;
}
namespace extensions {
class ExtensionDownloader;
class ManifestFetchData;
}
namespace net {
class URLFetcher;
}
namespace prerender {
bool IsOmniboxEnabled(Profile* profile);
}
namespace tracked_objects {
struct ProcessDataSnapshot;
}
// A Field Trial and its selected group, which represent a particular
// Chrome configuration state. For example, the trial name could map to
// a preference name, and the group name could map to a preference value.
struct SyntheticTrialGroup {
public:
~SyntheticTrialGroup();
chrome_variations::ActiveGroupId id;
base::TimeTicks start_time;
private:
friend class MetricsService;
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, RegisterSyntheticTrial);
// This constructor is private specifically so as to control which code is
// able to access it. New code that wishes to use it should be added as a
// friend class.
SyntheticTrialGroup(uint32 trial, uint32 group, base::TimeTicks start);
};
class MetricsService
: public chrome_browser_metrics::TrackingSynchronizerObserver,
public content::BrowserChildProcessObserver,
public content::NotificationObserver,
public net::URLFetcherDelegate,
public MetricsServiceBase {
public:
// The execution phase of the browser.
enum ExecutionPhase {
UNINITIALIZED_PHASE = 0,
START_METRICS_RECORDING = 100,
CREATE_PROFILE = 200,
STARTUP_TIMEBOMB_ARM = 300,
THREAD_WATCHER_START = 400,
MAIN_MESSAGE_LOOP_RUN = 500,
SHUTDOWN_TIMEBOMB_ARM = 600,
SHUTDOWN_COMPLETE = 700,
};
enum ReportingState {
REPORTING_ENABLED,
REPORTING_DISABLED,
};
MetricsService();
virtual ~MetricsService();
// Initializes metrics recording state. Updates various bookkeeping values in
// prefs and sets up the scheduler. This is a separate function rather than
// being done by the constructor so that field trials could be created before
// this is run. Takes |reporting_state| parameter which specifies whether UMA
// is enabled.
void InitializeMetricsRecordingState(ReportingState reporting_state);
// Starts the metrics system, turning on recording and uploading of metrics.
// Should be called when starting up with metrics enabled, or when metrics
// are turned on.
void Start();
// Starts the metrics system in a special test-only mode. Metrics won't ever
// be uploaded or persisted in this mode, but metrics will be recorded in
// memory.
void StartRecordingForTests();
// Shuts down the metrics system. Should be called at shutdown, or if metrics
// are turned off.
void Stop();
// Enable/disable transmission of accumulated logs and crash reports (dumps).
// Calling Start() automatically enables reporting, but sending is
// asyncronous so this can be called immediately after Start() to prevent
// any uploading.
void EnableReporting();
void DisableReporting();
// Returns the client ID for this client, or the empty string if metrics
// recording is not currently running.
std::string GetClientId();
// Returns the preferred entropy provider used to seed persistent activities
// based on whether or not metrics reporting will be permitted on this client.
// The caller must determine if metrics reporting will be enabled for this
// client and pass that state in as |reporting_will_be_enabled|.
//
// If |reporting_will_be_enabled| is true, this method returns an entropy
// provider that has a high source of entropy, partially based on the client
// ID. Otherwise, an entropy provider that is based on a low entropy source
// is returned.
//
// Note that this reporting state can not be checked by reporting_active()
// because this method may need to be called before the MetricsService needs
// to be started.
scoped_ptr<const base::FieldTrial::EntropyProvider> CreateEntropyProvider(
ReportingState reporting_state);
// Force the client ID to be generated. This is useful in case it's needed
// before recording.
void ForceClientIdCreation();
// At startup, prefs needs to be called with a list of all the pref names and
// types we'll be using.
static void RegisterPrefs(PrefRegistrySimple* registry);
// Set up notifications which indicate that a user is performing work. This is
// useful to allow some features to sleep, until the machine becomes active,
// such as precluding UMA uploads unless there was recent activity.
static void SetUpNotifications(content::NotificationRegistrar* registrar,
content::NotificationObserver* observer);
// Implementation of content::BrowserChildProcessObserver
virtual void BrowserChildProcessHostConnected(
const content::ChildProcessData& data) OVERRIDE;
virtual void BrowserChildProcessCrashed(
const content::ChildProcessData& data) OVERRIDE;
virtual void BrowserChildProcessInstanceCreated(
const content::ChildProcessData& data) OVERRIDE;
// Implementation of content::NotificationObserver
virtual void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
// Invoked when we get a WM_SESSIONEND. This places a value in prefs that is
// reset when RecordCompletedSessionEnd is invoked.
void RecordStartOfSessionEnd();
// This should be called when the application is shutting down. It records
// that session end was successful.
void RecordCompletedSessionEnd();
#if defined(OS_ANDROID) || defined(OS_IOS)
// Called when the application is going into background mode.
void OnAppEnterBackground();
// Called when the application is coming out of background mode.
void OnAppEnterForeground();
#else
// Set the dirty flag, which will require a later call to LogCleanShutdown().
static void LogNeedForCleanShutdown();
#endif // defined(OS_ANDROID) || defined(OS_IOS)
static void SetExecutionPhase(ExecutionPhase execution_phase);
// Saves in the preferences if the crash report registration was successful.
// This count is eventually send via UMA logs.
void RecordBreakpadRegistration(bool success);
// Saves in the preferences if the browser is running under a debugger.
// This count is eventually send via UMA logs.
void RecordBreakpadHasDebugger(bool has_debugger);
#if defined(OS_WIN)
// Counts (and removes) the browser crash dump attempt signals left behind by
// any previous browser processes which generated a crash dump.
void CountBrowserCrashDumpAttempts();
#endif // OS_WIN
#if defined(OS_CHROMEOS)
// Start the external metrics service, which collects metrics from Chrome OS
// and passes them to UMA.
void StartExternalMetrics();
// Records a Chrome OS crash.
void LogChromeOSCrash(const std::string &crash_type);
#endif
bool recording_active() const;
bool reporting_active() const;
void LogPluginLoadingError(const base::FilePath& plugin_path);
// Redundant test to ensure that we are notified of a clean exit.
// This value should be true when process has completed shutdown.
static bool UmaMetricsProperlyShutdown();
// Registers a field trial name and group to be used to annotate a UMA report
// with a particular Chrome configuration state. A UMA report will be
// annotated with this trial group if and only if all events in the report
// were created after the trial is registered. Only one group name may be
// registered at a time for a given trial_name. Only the last group name that
// is registered for a given trial name will be recorded. The values passed
// in must not correspond to any real field trial in the code.
// To use this method, SyntheticTrialGroup should friend your class.
void RegisterSyntheticFieldTrial(const SyntheticTrialGroup& trial_group);
private:
// The MetricsService has a lifecycle that is stored as a state.
// See metrics_service.cc for description of this lifecycle.
enum State {
INITIALIZED, // Constructor was called.
INIT_TASK_SCHEDULED, // Waiting for deferred init tasks to
// complete.
INIT_TASK_DONE, // Waiting for timer to send initial log.
SENDING_INITIAL_STABILITY_LOG, // Initial stability log being sent.
SENDING_INITIAL_METRICS_LOG, // Initial metrics log being sent.
SENDING_OLD_LOGS, // Sending unsent logs from last session.
SENDING_CURRENT_LOGS, // Sending ongoing logs as they accrue.
};
enum ShutdownCleanliness {
CLEANLY_SHUTDOWN = 0xdeadbeef,
NEED_TO_SHUTDOWN = ~CLEANLY_SHUTDOWN
};
// Designates which entropy source was returned from this MetricsService.
// This is used for testing to validate that we return the correct source
// depending on the state of the service.
enum EntropySourceReturned {
LAST_ENTROPY_NONE,
LAST_ENTROPY_LOW,
LAST_ENTROPY_HIGH,
};
struct ChildProcessStats;
typedef std::vector<SyntheticTrialGroup> SyntheticTrialGroups;
// First part of the init task. Called on the FILE thread to load hardware
// class information.
static void InitTaskGetHardwareClass(base::WeakPtr<MetricsService> self,
base::MessageLoopProxy* target_loop);
// Callback from InitTaskGetHardwareClass() that continues the init task by
// loading plugin information.
void OnInitTaskGotHardwareClass(const std::string& hardware_class);
// Callback from PluginService::GetPlugins() that continues the init task by
// launching a task to gather Google Update statistics.
void OnInitTaskGotPluginInfo(
const std::vector<content::WebPluginInfo>& plugins);
// Task launched by OnInitTaskGotPluginInfo() that continues the init task by
// loading Google Update statistics. Called on a blocking pool thread.
static void InitTaskGetGoogleUpdateData(base::WeakPtr<MetricsService> self,
base::MessageLoopProxy* target_loop);
// Callback from InitTaskGetGoogleUpdateData() that continues the init task by
// loading profiler data.
void OnInitTaskGotGoogleUpdateData(
const GoogleUpdateMetrics& google_update_metrics);
void OnUserAction(const std::string& action);
// TrackingSynchronizerObserver:
virtual void ReceivedProfilerData(
const tracked_objects::ProcessDataSnapshot& process_data,
int process_type) OVERRIDE;
// Callback that moves the state to INIT_TASK_DONE.
virtual void FinishedReceivingProfilerData() OVERRIDE;
// Get the amount of uptime since this function was last called.
// This updates the cumulative uptime metric for uninstall as a side effect.
base::TimeDelta GetIncrementalUptime(PrefService* pref);
// Returns the low entropy source for this client. This is a random value
// that is non-identifying amongst browser clients. This method will
// generate the entropy source value if it has not been called before.
int GetLowEntropySource();
// Returns the first entropy source that was returned by this service since
// start up, or NONE if neither was returned yet. This is exposed for testing
// only.
EntropySourceReturned entropy_source_returned() const {
return entropy_source_returned_;
}
// When we start a new version of Chromium (different from our last run), we
// need to discard the old crash stats so that we don't attribute crashes etc.
// in the old version to the current version (via current logs).
// Without this, a common reason to finally start a new version is to crash
// the old version (after an autoupdate has arrived), and so we'd bias
// initial results towards showing crashes :-(.
static void DiscardOldStabilityStats(PrefService* local_state);
// Turns recording on or off.
// DisableRecording() also forces a persistent save of logging state (if
// anything has been recorded, or transmitted).
void EnableRecording();
void DisableRecording();
// If in_idle is true, sets idle_since_last_transmission to true.
// If in_idle is false and idle_since_last_transmission_ is true, sets
// idle_since_last_transmission to false and starts the timer (provided
// starting the timer is permitted).
void HandleIdleSinceLastTransmission(bool in_idle);
// Set up client ID, session ID, etc.
void InitializeMetricsState(ReportingState reporting_state);
// Generates a new client ID to use to identify self to metrics server.
static std::string GenerateClientID();
// Schedule the next save of LocalState information. This is called
// automatically by the task that performs each save to schedule the next one.
void ScheduleNextStateSave();
// Save the LocalState information immediately. This should not be called by
// anybody other than the scheduler to avoid doing too many writes. When you
// make a change, call ScheduleNextStateSave() instead.
void SaveLocalState();
// Opens a new log for recording user experience metrics.
void OpenNewLog();
// Closes out the current log after adding any last information.
void CloseCurrentLog();
// Pushes the text of the current and staged logs into persistent storage.
// Called when Chrome shuts down.
void PushPendingLogsToPersistentStorage();
// Ensures that scheduler is running, assuming the current settings are such
// that metrics should be reported. If not, this is a no-op.
void StartSchedulerIfNecessary();
// Starts the process of uploading metrics data.
void StartScheduledUpload();
// Starts collecting any data that should be added to a log just before it is
// closed.
void StartFinalLogInfoCollection();
// Callbacks for various stages of final log info collection. Do not call
// these directly.
void OnMemoryDetailCollectionDone();
void OnHistogramSynchronizationDone();
void OnFinalLogInfoCollectionDone();
// Either closes the current log or creates and closes the initial log
// (depending on |state_|), and stages it for upload.
void StageNewLog();
// Prepares the initial stability log, which is only logged when the previous
// run of Chrome crashed. This log contains any stability metrics left over
// from that previous run, and only these stability metrics. It uses the
// system profile from the previous session.
void PrepareInitialStabilityLog();
// Prepares the initial metrics log, which includes startup histograms and
// profiler data, as well as incremental stability-related metrics.
void PrepareInitialMetricsLog(MetricsLog::LogType log_type);
// Uploads the currently staged log (which must be non-null).
void SendStagedLog();
// Prepared the staged log to be passed to the server. Upon return,
// current_fetch_ should be reset with its upload data set to a compressed
// copy of the staged log.
void PrepareFetchWithStagedLog();
// Implementation of net::URLFetcherDelegate. Called after transmission
// completes (either successfully or with failure).
virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
// Reads, increments and then sets the specified integer preference.
void IncrementPrefValue(const char* path);
// Reads, increments and then sets the specified long preference that is
// stored as a string.
void IncrementLongPrefsValue(const char* path);
// Records a renderer process crash.
void LogRendererCrash(content::RenderProcessHost* host,
base::TerminationStatus status,
int exit_code);
// Records a renderer process hang.
void LogRendererHang();
// Records that the browser was shut down cleanly.
void LogCleanShutdown();
// Returns reference to ChildProcessStats corresponding to |data|.
ChildProcessStats& GetChildProcessStats(
const content::ChildProcessData& data);
// Saves plugin-related updates from the in-object buffer to Local State
// for retrieval next time we send a Profile log (generally next launch).
void RecordPluginChanges(PrefService* pref);
// Records state that should be periodically saved, like uptime and
// buffered plugin stability statistics.
void RecordCurrentState(PrefService* pref);
// Logs the initiation of a page load and uses |web_contents| to do
// additional logging of the type of page loaded.
void LogLoadStarted(content::WebContents* web_contents);
// Checks whether a notification can be logged.
bool CanLogNotification();
// Sets the value of the specified path in prefs and schedules a save.
void RecordBooleanPrefValue(const char* path, bool value);
// Returns true if process of type |type| should be counted as a plugin
// process, and false otherwise.
static bool IsPluginProcess(int process_type);
// Returns a list of synthetic field trials that were active for the entire
// duration of the current log.
void GetCurrentSyntheticFieldTrials(
std::vector<chrome_variations::ActiveGroupId>* synthetic_trials);
content::ActionCallback action_callback_;
content::NotificationRegistrar registrar_;
// Indicate whether recording and reporting are currently happening.
// These should not be set directly, but by calling SetRecording and
// SetReporting.
bool recording_active_;
bool reporting_active_;
// Indicate whether test mode is enabled, where the initial log should never
// be cut, and logs are neither persisted nor uploaded.
bool test_mode_active_;
// The progression of states made by the browser are recorded in the following
// state.
State state_;
// Whether the initial stability log has been recorded during startup.
bool has_initial_stability_log_;
// Chrome OS hardware class (e.g., hardware qualification ID). This
// class identifies the configured system components such as CPU,
// WiFi adapter, etc. For non Chrome OS hosts, this will be an
// empty string.
std::string hardware_class_;
// The list of plugins which was retrieved on the file thread.
std::vector<content::WebPluginInfo> plugins_;
// Google Update statistics, which were retrieved on a blocking pool thread.
GoogleUpdateMetrics google_update_metrics_;
// The initial metrics log, used to record startup metrics (histograms and
// profiler data). Note that if a crash occurred in the previous session, an
// initial stability log may be sent before this.
scoped_ptr<MetricsLog> initial_metrics_log_;
// The outstanding transmission appears as a URL Fetch operation.
scoped_ptr<net::URLFetcher> current_fetch_;
// The TCP/UDP echo server to collect network connectivity stats.
std::string network_stats_server_;
// The HTTP pipelining test server.
std::string http_pipelining_test_server_;
// The identifier that's sent to the server with the log reports.
std::string client_id_;
// The non-identifying low entropy source value.
int low_entropy_source_;
// Whether the MetricsService object has received any notifications since
// the last time a transmission was sent.
bool idle_since_last_transmission_;
// A number that identifies the how many times the app has been launched.
int session_id_;
// Maps WebContentses (corresponding to tabs) or Browsers (corresponding to
// Windows) to a unique integer that we will use to identify them.
// |next_window_id_| is used to track which IDs we have used so far.
typedef std::map<uintptr_t, int> WindowMap;
WindowMap window_map_;
int next_window_id_;
// Buffer of child process notifications for quick access.
std::map<base::string16, ChildProcessStats> child_process_stats_buffer_;
// Weak pointers factory used to post task on different threads. All weak
// pointers managed by this factory have the same lifetime as MetricsService.
base::WeakPtrFactory<MetricsService> self_ptr_factory_;
// Weak pointers factory used for saving state. All weak pointers managed by
// this factory are invalidated in ScheduleNextStateSave.
base::WeakPtrFactory<MetricsService> state_saver_factory_;
// The scheduler for determining when uploads should happen.
scoped_ptr<MetricsReportingScheduler> scheduler_;
// Indicates that an asynchronous reporting step is running.
// This is used only for debugging.
bool waiting_for_asynchronous_reporting_step_;
// Number of async histogram fetch requests in progress.
int num_async_histogram_fetches_in_progress_;
#if defined(OS_CHROMEOS)
// The external metric service is used to log ChromeOS UMA events.
scoped_refptr<chromeos::ExternalMetrics> external_metrics_;
#endif
// The last entropy source returned by this service, used for testing.
EntropySourceReturned entropy_source_returned_;
// Stores the time of the last call to |GetIncrementalUptime()|.
base::TimeTicks last_updated_time_;
// Execution phase the browser is in.
static ExecutionPhase execution_phase_;
// Reduntant marker to check that we completed our shutdown, and set the
// exited-cleanly bit in the prefs.
static ShutdownCleanliness clean_shutdown_status_;
// Field trial groups that map to Chrome configuration states.
SyntheticTrialGroups synthetic_trial_groups_;
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, ClientIdCorrectlyFormatted);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, IsPluginProcess);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, LowEntropySource0NotReset);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest,
PermutedEntropyCacheClearedWhenLowEntropyReset);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, RegisterSyntheticTrial);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceBrowserTest,
CheckLowEntropySourceUsed);
FRIEND_TEST_ALL_PREFIXES(MetricsServiceReportingTest,
CheckHighEntropySourceUsed);
DISALLOW_COPY_AND_ASSIGN(MetricsService);
};
// This class limits and documents access to the IsMetricsReportingEnabled()
// method. Since the method is private, each user has to be explicitly declared
// as a 'friend' below.
class MetricsServiceHelper {
private:
friend bool prerender::IsOmniboxEnabled(Profile* profile);
friend class extensions::ExtensionDownloader;
friend class extensions::ManifestFetchData;
// Returns true if prefs::kMetricsReportingEnabled is set.
static bool IsMetricsReportingEnabled();
DISALLOW_IMPLICIT_CONSTRUCTORS(MetricsServiceHelper);
};
#endif // CHROME_BROWSER_METRICS_METRICS_SERVICE_H_