#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
-#include "base/guid.h"
-#include "base/md5.h"
#include "base/metrics/histogram.h"
#include "base/metrics/sparse_histogram.h"
#include "base/metrics/statistics_recorder.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
#include "base/prefs/scoped_user_pref_update.h"
-#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/platform_thread.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/io_thread.h"
#include "chrome/browser/memory_details.h"
-#include "chrome/browser/metrics/cloned_install_detector.h"
#include "chrome/browser/metrics/compression_utils.h"
-#include "chrome/browser/metrics/machine_id_provider.h"
#include "chrome/browser/metrics/metrics_log.h"
#include "chrome/browser/metrics/metrics_log_serializer.h"
#include "chrome/browser/metrics/metrics_reporting_scheduler.h"
+#include "chrome/browser/metrics/metrics_state_manager.h"
#include "chrome/browser/metrics/time_ticks_experiment_win.h"
#include "chrome/browser/metrics/tracking_synchronizer.h"
#include "chrome/common/metrics/variations/variations_util.h"
#include "chrome/common/chrome_result_codes.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/crash_keys.h"
-#include "chrome/common/metrics/caching_permuted_entropy_provider.h"
-#include "chrome/common/metrics/metrics_log_manager.h"
#include "chrome/common/net/test_server_locations.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
+#include "components/metrics/metrics_log_manager.h"
#include "components/variations/entropy_provider.h"
#include "components/variations/metrics_util.h"
#include "content/public/browser/child_process_data.h"
using content::ChildProcessData;
using content::LoadNotificationDetails;
using content::PluginService;
+using metrics::MetricsLogManager;
namespace {
}
}
-// The argument used to generate a non-identifying entropy source. We want no
-// more than 13 bits of entropy, so use this max to return a number in the range
-// [0, 7999] as the entropy source (12.97 bits of entropy).
-const int kMaxLowEntropySize = 8000;
-
-// Default prefs value for prefs::kMetricsLowEntropySource to indicate that the
-// value has not yet been set.
-const int kLowEntropySourceNotSet = -1;
-
-// Generates a new non-identifying entropy source used to seed persistent
-// activities.
-int GenerateLowEntropySource() {
- return base::RandInt(0, kMaxLowEntropySize - 1);
-}
-
// Converts an exit code into something that can be inserted into our
// histograms (which expect non-negative numbers less than MAX_INT).
int MapCrashExitCodeForHistogram(int exit_code) {
pref->CommitPendingWrite();
}
-// Returns whether initial stability metrics should be sent in a separate log.
-bool SendSeparateInitialStabilityLog() {
- return base::FieldTrialList::FindFullName("UMAStability") == "SeparateLog";
-}
-
} // namespace
// static
void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
DCHECK(IsSingleThreaded());
- registry->RegisterBooleanPref(prefs::kMetricsResetIds, false);
- registry->RegisterStringPref(prefs::kMetricsClientID, std::string());
- registry->RegisterInt64Pref(prefs::kMetricsReportingEnabledTimestamp, 0);
- registry->RegisterIntegerPref(prefs::kMetricsLowEntropySource,
- kLowEntropySourceNotSet);
+ metrics::MetricsStateManager::RegisterPrefs(registry);
+
registry->RegisterInt64Pref(prefs::kStabilityLaunchTimeSec, 0);
registry->RegisterInt64Pref(prefs::kStabilityLastTimestampSec, 0);
registry->RegisterStringPref(prefs::kStabilityStatsVersion, std::string());
registry->RegisterInt64Pref(prefs::kUninstallLastLaunchTimeSec, 0);
registry->RegisterInt64Pref(prefs::kUninstallLastObservedRunTimeSec, 0);
- // TODO(asvitkine): Remove these once a couple of releases have passed.
- // http://crbug.com/357704
- registry->RegisterStringPref(prefs::kMetricsOldClientID, std::string());
- registry->RegisterIntegerPref(prefs::kMetricsOldLowEntropySource, 0);
-
#if defined(OS_ANDROID)
RegisterPrefsAndroid(registry);
#endif // defined(OS_ANDROID)
}
-// static
-void MetricsService::DiscardOldStabilityStats(PrefService* local_state) {
- local_state->SetBoolean(prefs::kStabilityExitedCleanly, true);
- local_state->SetInteger(prefs::kStabilityExecutionPhase, UNINITIALIZED_PHASE);
- local_state->SetBoolean(prefs::kStabilitySessionEndCompleted, true);
-
- local_state->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0);
- local_state->SetInteger(prefs::kStabilityBreakpadRegistrationSuccess, 0);
- local_state->SetInteger(prefs::kStabilityBreakpadRegistrationFail, 0);
- local_state->SetInteger(prefs::kStabilityDebuggerPresent, 0);
- local_state->SetInteger(prefs::kStabilityDebuggerNotPresent, 0);
-
- local_state->SetInteger(prefs::kStabilityLaunchCount, 0);
- local_state->SetInteger(prefs::kStabilityCrashCount, 0);
-
- local_state->SetInteger(prefs::kStabilityPageLoadCount, 0);
- local_state->SetInteger(prefs::kStabilityRendererCrashCount, 0);
- local_state->SetInteger(prefs::kStabilityRendererHangCount, 0);
-
- local_state->SetInt64(prefs::kStabilityLaunchTimeSec, 0);
- local_state->SetInt64(prefs::kStabilityLastTimestampSec, 0);
-
- local_state->ClearPref(prefs::kStabilityPluginStats);
-
- local_state->ClearPref(prefs::kMetricsInitialLogs);
- local_state->ClearPref(prefs::kMetricsOngoingLogs);
-
-#if defined(OS_ANDROID)
- DiscardOldStabilityStatsAndroid(local_state);
-#endif // defined(OS_ANDROID)
-}
-
-MetricsService::MetricsService()
- : metrics_ids_reset_check_performed_(false),
+MetricsService::MetricsService(metrics::MetricsStateManager* state_manager)
+ : state_manager_(state_manager),
recording_active_(false),
reporting_active_(false),
test_mode_active_(false),
state_(INITIALIZED),
has_initial_stability_log_(false),
- low_entropy_source_(kLowEntropySourceNotSet),
idle_since_last_transmission_(false),
session_id_(-1),
next_window_id_(0),
self_ptr_factory_(this),
state_saver_factory_(this),
waiting_for_asynchronous_reporting_step_(false),
- num_async_histogram_fetches_in_progress_(0),
- entropy_source_returned_(LAST_ENTROPY_NONE) {
+ num_async_histogram_fetches_in_progress_(0) {
DCHECK(IsSingleThreaded());
+ DCHECK(state_manager_);
log_manager_.set_log_serializer(new MetricsLogSerializer);
log_manager_.set_max_ongoing_log_store_size(kUploadLogAvoidRetransmitSize);
BrowserChildProcessObserver::Remove(this);
}
-void MetricsService::InitializeMetricsRecordingState(
- ReportingState reporting_state) {
- InitializeMetricsState(reporting_state);
+void MetricsService::InitializeMetricsRecordingState() {
+ InitializeMetricsState();
base::Closure callback = base::Bind(&MetricsService::StartScheduledUpload,
self_ptr_factory_.GetWeakPtr());
EnableReporting();
}
+bool MetricsService::StartIfMetricsReportingEnabled() {
+ const bool enabled = state_manager_->IsMetricsReportingEnabled();
+ if (enabled)
+ Start();
+ return enabled;
+}
+
void MetricsService::StartRecordingForTests() {
test_mode_active_ = true;
EnableRecording();
}
std::string MetricsService::GetClientId() {
- return client_id_;
+ return state_manager_->client_id();
}
scoped_ptr<const base::FieldTrial::EntropyProvider>
-MetricsService::CreateEntropyProvider(ReportingState reporting_state) {
- // For metrics reporting-enabled users, we combine the client ID and low
- // entropy source to get the final entropy source. Otherwise, only use the low
- // entropy source.
- // This has two useful properties:
- // 1) It makes the entropy source less identifiable for parties that do not
- // know the low entropy source.
- // 2) It makes the final entropy source resettable.
- const int low_entropy_source_value = GetLowEntropySource();
- UMA_HISTOGRAM_SPARSE_SLOWLY("UMA.LowEntropySourceValue",
- low_entropy_source_value);
- if (reporting_state == REPORTING_ENABLED) {
- if (entropy_source_returned_ == LAST_ENTROPY_NONE)
- entropy_source_returned_ = LAST_ENTROPY_HIGH;
- DCHECK_EQ(LAST_ENTROPY_HIGH, entropy_source_returned_);
- const std::string high_entropy_source =
- client_id_ + base::IntToString(low_entropy_source_value);
- return scoped_ptr<const base::FieldTrial::EntropyProvider>(
- new metrics::SHA1EntropyProvider(high_entropy_source));
- }
-
- if (entropy_source_returned_ == LAST_ENTROPY_NONE)
- entropy_source_returned_ = LAST_ENTROPY_LOW;
- DCHECK_EQ(LAST_ENTROPY_LOW, entropy_source_returned_);
-
-#if defined(OS_ANDROID) || defined(OS_IOS)
- return scoped_ptr<const base::FieldTrial::EntropyProvider>(
- new metrics::CachingPermutedEntropyProvider(
- g_browser_process->local_state(),
- low_entropy_source_value,
- kMaxLowEntropySize));
-#else
- return scoped_ptr<const base::FieldTrial::EntropyProvider>(
- new metrics::PermutedEntropyProvider(low_entropy_source_value,
- kMaxLowEntropySize));
-#endif
-}
-
-void MetricsService::ForceClientIdCreation() {
- if (!client_id_.empty())
- return;
-
- ResetMetricsIDsIfNecessary();
-
- PrefService* pref = g_browser_process->local_state();
- client_id_ = pref->GetString(prefs::kMetricsClientID);
- if (!client_id_.empty())
- return;
-
- client_id_ = GenerateClientID();
- pref->SetString(prefs::kMetricsClientID, client_id_);
-
- if (pref->GetString(prefs::kMetricsOldClientID).empty()) {
- // Record the timestamp of when the user opted in to UMA.
- pref->SetInt64(prefs::kMetricsReportingEnabledTimestamp,
- Time::Now().ToTimeT());
- } else {
- UMA_HISTOGRAM_BOOLEAN("UMA.ClientIdMigrated", true);
- }
- pref->ClearPref(prefs::kMetricsOldClientID);
+MetricsService::CreateEntropyProvider() {
+ // TODO(asvitkine): Refactor the code so that MetricsService does not expose
+ // this method.
+ return state_manager_->CreateEntropyProvider();
}
void MetricsService::EnableRecording() {
return;
recording_active_ = true;
- ForceClientIdCreation();
- crash_keys::SetClientID(client_id_);
+ state_manager_->ForceClientIdCreation();
+ crash_keys::SetClientID(state_manager_->client_id());
if (!log_manager_.current_log())
OpenNewLog();
//------------------------------------------------------------------------------
// Initialization methods
-void MetricsService::InitializeMetricsState(ReportingState reporting_state) {
+void MetricsService::InitializeMetricsState() {
#if defined(OS_POSIX)
network_stats_server_ = chrome_common_net::kEchoTestServerLocation;
http_pipelining_test_server_ = chrome_common_net::kPipelineTestServerBaseUrl;
PrefService* pref = g_browser_process->local_state();
DCHECK(pref);
- // TODO(asvitkine): Kill this logic when SendSeparateInitialStabilityLog() is
- // is made the default behavior.
- if ((pref->GetInt64(prefs::kStabilityStatsBuildTime)
- != MetricsLog::GetBuildTime()) ||
- (pref->GetString(prefs::kStabilityStatsVersion)
- != MetricsLog::GetVersionString())) {
- // This is a new version, so we don't want to confuse the stats about the
- // old version with info that we upload.
- DiscardOldStabilityStats(pref);
- pref->SetString(prefs::kStabilityStatsVersion,
- MetricsLog::GetVersionString());
- pref->SetInt64(prefs::kStabilityStatsBuildTime,
- MetricsLog::GetBuildTime());
- }
+ pref->SetString(prefs::kStabilityStatsVersion,
+ MetricsLog::GetVersionString());
+ pref->SetInt64(prefs::kStabilityStatsBuildTime, MetricsLog::GetBuildTime());
session_id_ = pref->GetInteger(prefs::kMetricsSessionID);
// If the previous session didn't exit cleanly, then prepare an initial
// stability log if UMA is enabled.
- bool reporting_will_be_enabled = (reporting_state == REPORTING_ENABLED);
- if (reporting_will_be_enabled && SendSeparateInitialStabilityLog())
+ if (state_manager_->IsMetricsReportingEnabled())
PrepareInitialStabilityLog();
}
if (!ShouldLogEvents())
return;
- log_manager_.current_log()->RecordUserAction(action.c_str());
+ log_manager_.current_log()->RecordUserAction(action);
HandleIdleSinceLastTransmission(false);
}
// Upon the first callback, create the initial log so that we can immediately
// save the profiler data.
- if (!initial_metrics_log_.get())
- initial_metrics_log_.reset(new MetricsLog(client_id_, session_id_));
+ if (!initial_metrics_log_.get()) {
+ initial_metrics_log_.reset(
+ new MetricsLog(state_manager_->client_id(), session_id_,
+ MetricsLog::ONGOING_LOG));
+ NotifyOnDidCreateMetricsLog();
+ }
initial_metrics_log_->RecordProfilerData(process_data, process_type);
}
}
}
-void MetricsService::ResetMetricsIDsIfNecessary() {
- if (metrics_ids_reset_check_performed_)
- return;
-
- metrics_ids_reset_check_performed_ = true;
-
- PrefService* local_state = g_browser_process->local_state();
- if (!local_state->GetBoolean(prefs::kMetricsResetIds))
- return;
-
- UMA_HISTOGRAM_BOOLEAN("UMA.MetricsIDsReset", true);
-
- DCHECK(client_id_.empty());
- DCHECK_EQ(kLowEntropySourceNotSet, low_entropy_source_);
-
- local_state->ClearPref(prefs::kMetricsClientID);
- local_state->ClearPref(prefs::kMetricsLowEntropySource);
- local_state->ClearPref(prefs::kMetricsResetIds);
+void MetricsService::AddObserver(MetricsServiceObserver* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ observers_.AddObserver(observer);
}
-int MetricsService::GetLowEntropySource() {
- // Note that the default value for the low entropy source and the default pref
- // value are both kLowEntropySourceNotSet, which is used to identify if the
- // value has been set or not.
- if (low_entropy_source_ != kLowEntropySourceNotSet)
- return low_entropy_source_;
-
- ResetMetricsIDsIfNecessary();
-
- PrefService* local_state = g_browser_process->local_state();
- const CommandLine* command_line(CommandLine::ForCurrentProcess());
- // Only try to load the value from prefs if the user did not request a reset.
- // Otherwise, skip to generating a new value.
- if (!command_line->HasSwitch(switches::kResetVariationState)) {
- int value = local_state->GetInteger(prefs::kMetricsLowEntropySource);
- // If the value is outside the [0, kMaxLowEntropySize) range, re-generate
- // it below.
- if (value >= 0 && value < kMaxLowEntropySize) {
- low_entropy_source_ = value;
- UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", false);
- return low_entropy_source_;
- }
- }
-
- UMA_HISTOGRAM_BOOLEAN("UMA.GeneratedLowEntropySource", true);
- low_entropy_source_ = GenerateLowEntropySource();
- local_state->SetInteger(prefs::kMetricsLowEntropySource, low_entropy_source_);
- local_state->ClearPref(prefs::kMetricsOldLowEntropySource);
- metrics::CachingPermutedEntropyProvider::ClearCache(local_state);
-
- return low_entropy_source_;
+void MetricsService::RemoveObserver(MetricsServiceObserver* observer) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ observers_.RemoveObserver(observer);
}
-// static
-std::string MetricsService::GenerateClientID() {
- return base::GenerateGUID();
+void MetricsService::NotifyOnDidCreateMetricsLog() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ FOR_EACH_OBSERVER(
+ MetricsServiceObserver, observers_, OnDidCreateMetricsLog());
}
//------------------------------------------------------------------------------
void MetricsService::OpenNewLog() {
DCHECK(!log_manager_.current_log());
- log_manager_.BeginLoggingWithLog(new MetricsLog(client_id_, session_id_),
- MetricsLog::ONGOING_LOG);
+ log_manager_.BeginLoggingWithLog(
+ new MetricsLog(state_manager_->client_id(), session_id_,
+ MetricsLog::ONGOING_LOG));
+ NotifyOnDidCreateMetricsLog();
if (state_ == INITIALIZED) {
// We only need to schedule that run once.
state_ = INIT_TASK_SCHEDULED;
base::TimeDelta incremental_uptime;
base::TimeDelta uptime;
GetUptimes(pref, &incremental_uptime, &uptime);
- current_log->RecordStabilityMetrics(incremental_uptime, uptime,
- MetricsLog::ONGOING_LOG);
+ current_log->RecordStabilityMetrics(incremental_uptime, uptime);
RecordCurrentHistograms();
// logs have already been loaded by PrepareInitialStabilityLog().
state_ = SENDING_INITIAL_STABILITY_LOG;
} else {
- // TODO(asvitkine): When the field trial is removed, the |log_type|
- // arg should be removed and PrepareInitialMetricsLog() should always
- // use ONGOING_LOG. Use INITIAL_LOG only to match to the old behavior
- // when the field trial is off.
- MetricsLog::LogType log_type = SendSeparateInitialStabilityLog() ?
- MetricsLog::ONGOING_LOG : MetricsLog::INITIAL_LOG;
- PrepareInitialMetricsLog(log_type);
+ PrepareInitialMetricsLog();
// Load unsent logs (if any) from local state.
log_manager_.LoadPersistedUnsentLogs();
state_ = SENDING_INITIAL_METRICS_LOG;
DCHECK_NE(0, pref->GetInteger(prefs::kStabilityCrashCount));
scoped_ptr<MetricsLog> initial_stability_log(
- new MetricsLog(client_id_, session_id_));
+ new MetricsLog(state_manager_->client_id(), session_id_,
+ MetricsLog::INITIAL_STABILITY_LOG));
+
+ // Do not call NotifyOnDidCreateMetricsLog here because the stability
+ // log describes stats from the _previous_ session.
+
if (!initial_stability_log->LoadSavedEnvironmentFromPrefs())
return;
- initial_stability_log->RecordStabilityMetrics(
- base::TimeDelta(), base::TimeDelta(), MetricsLog::INITIAL_LOG);
+ initial_stability_log->RecordStabilityMetrics(base::TimeDelta(),
+ base::TimeDelta());
log_manager_.LoadPersistedUnsentLogs();
log_manager_.PauseCurrentLog();
- log_manager_.BeginLoggingWithLog(initial_stability_log.release(),
- MetricsLog::INITIAL_LOG);
+ log_manager_.BeginLoggingWithLog(initial_stability_log.release());
#if defined(OS_ANDROID)
ConvertAndroidStabilityPrefsToHistograms(pref);
RecordCurrentStabilityHistograms();
has_initial_stability_log_ = true;
}
-void MetricsService::PrepareInitialMetricsLog(MetricsLog::LogType log_type) {
+void MetricsService::PrepareInitialMetricsLog() {
DCHECK(state_ == INIT_TASK_DONE || state_ == SENDING_INITIAL_STABILITY_LOG);
initial_metrics_log_->set_hardware_class(hardware_class_);
base::TimeDelta incremental_uptime;
base::TimeDelta uptime;
GetUptimes(pref, &incremental_uptime, &uptime);
- initial_metrics_log_->RecordStabilityMetrics(incremental_uptime, uptime,
- log_type);
+ initial_metrics_log_->RecordStabilityMetrics(incremental_uptime, uptime);
// Histograms only get written to the current log, so make the new log current
// before writing them.
log_manager_.PauseCurrentLog();
- log_manager_.BeginLoggingWithLog(initial_metrics_log_.release(), log_type);
+ log_manager_.BeginLoggingWithLog(initial_metrics_log_.release());
#if defined(OS_ANDROID)
ConvertAndroidStabilityPrefsToHistograms(pref);
#endif // defined(OS_ANDROID)
case SENDING_INITIAL_STABILITY_LOG:
// Store the updated list to disk now that the removed log is uploaded.
log_manager_.PersistUnsentLogs();
- PrepareInitialMetricsLog(MetricsLog::ONGOING_LOG);
+ PrepareInitialMetricsLog();
SendStagedLog();
state_ = SENDING_INITIAL_METRICS_LOG;
break;
}
void MetricsService::CheckForClonedInstall() {
- DCHECK(!cloned_install_detector_);
-
- metrics::MachineIdProvider* provider =
- metrics::MachineIdProvider::CreateInstance();
- if (!provider)
- return;
-
- cloned_install_detector_.reset(
- new metrics::ClonedInstallDetector(provider));
-
- PrefService* local_state = g_browser_process->local_state();
- cloned_install_detector_->CheckForClonedInstall(local_state);
+ state_manager_->CheckForClonedInstall();
}
void MetricsService::GetCurrentSyntheticFieldTrials(
return false;
#endif
}
+
+void MetricsServiceHelper::AddMetricsServiceObserver(
+ MetricsServiceObserver* observer) {
+ MetricsService* metrics_service = g_browser_process->metrics_service();
+ if (metrics_service)
+ metrics_service->AddObserver(observer);
+}
+
+void MetricsServiceHelper::RemoveMetricsServiceObserver(
+ MetricsServiceObserver* observer) {
+ MetricsService* metrics_service = g_browser_process->metrics_service();
+ if (metrics_service)
+ metrics_service->RemoveObserver(observer);
+}