1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5//------------------------------------------------------------------------------ 6// Description of the life cycle of a instance of MetricsService. 7// 8// OVERVIEW 9// 10// A MetricsService instance is typically created at application startup. It is 11// the central controller for the acquisition of log data, and the automatic 12// transmission of that log data to an external server. Its major job is to 13// manage logs, grouping them for transmission, and transmitting them. As part 14// of its grouping, MS finalizes logs by including some just-in-time gathered 15// memory statistics, snapshotting the current stats of numerous histograms, 16// closing the logs, translating to protocol buffer format, and compressing the 17// results for transmission. Transmission includes submitting a compressed log 18// as data in a URL-post, and retransmitting (or retaining at process 19// termination) if the attempted transmission failed. Retention across process 20// terminations is done using the the PrefServices facilities. The retained logs 21// (the ones that never got transmitted) are compressed and base64-encoded 22// before being persisted. 23// 24// Logs fall into one of two categories: "initial logs," and "ongoing logs." 25// There is at most one initial log sent for each complete run of Chrome (from 26// startup, to browser shutdown). An initial log is generally transmitted some 27// short time (1 minute?) after startup, and includes stats such as recent crash 28// info, the number and types of plugins, etc. The external server's response 29// to the initial log conceptually tells this MS if it should continue 30// transmitting logs (during this session). The server response can actually be 31// much more detailed, and always includes (at a minimum) how often additional 32// ongoing logs should be sent. 33// 34// After the above initial log, a series of ongoing logs will be transmitted. 35// The first ongoing log actually begins to accumulate information stating when 36// the MS was first constructed. Note that even though the initial log is 37// commonly sent a full minute after startup, the initial log does not include 38// much in the way of user stats. The most common interlog period (delay) 39// is 30 minutes. That time period starts when the first user action causes a 40// logging event. This means that if there is no user action, there may be long 41// periods without any (ongoing) log transmissions. Ongoing logs typically 42// contain very detailed records of user activities (ex: opened tab, closed 43// tab, fetched URL, maximized window, etc.) In addition, just before an 44// ongoing log is closed out, a call is made to gather memory statistics. Those 45// memory statistics are deposited into a histogram, and the log finalization 46// code is then called. In the finalization, a call to a Histogram server 47// acquires a list of all local histograms that have been flagged for upload 48// to the UMA server. The finalization also acquires the most recent number 49// of page loads, along with any counts of renderer or plugin crashes. 50// 51// When the browser shuts down, there will typically be a fragment of an ongoing 52// log that has not yet been transmitted. At shutdown time, that fragment is 53// closed (including snapshotting histograms), and persisted, for potential 54// transmission during a future run of the product. 55// 56// There are two slightly abnormal shutdown conditions. There is a 57// "disconnected scenario," and a "really fast startup and shutdown" scenario. 58// In the "never connected" situation, the user has (during the running of the 59// process) never established an internet connection. As a result, attempts to 60// transmit the initial log have failed, and a lot(?) of data has accumulated in 61// the ongoing log (which didn't yet get closed, because there was never even a 62// contemplation of sending it). There is also a kindred "lost connection" 63// situation, where a loss of connection prevented an ongoing log from being 64// transmitted, and a (still open) log was stuck accumulating a lot(?) of data, 65// while the earlier log retried its transmission. In both of these 66// disconnected situations, two logs need to be, and are, persistently stored 67// for future transmission. 68// 69// The other unusual shutdown condition, termed "really fast startup and 70// shutdown," involves the deliberate user termination of the process before 71// the initial log is even formed or transmitted. In that situation, no logging 72// is done, but the historical crash statistics remain (unlogged) for inclusion 73// in a future run's initial log. (i.e., we don't lose crash stats). 74// 75// With the above overview, we can now describe the state machine's various 76// states, based on the State enum specified in the state_ member. Those states 77// are: 78// 79// INITIALIZED, // Constructor was called. 80// INIT_TASK_SCHEDULED, // Waiting for deferred init tasks to finish. 81// INIT_TASK_DONE, // Waiting for timer to send initial log. 82// SENDING_INITIAL_STABILITY_LOG, // Initial stability log being sent. 83// SENDING_INITIAL_METRICS_LOG, // Initial metrics log being sent. 84// SENDING_OLD_LOGS, // Sending unsent logs from previous session. 85// SENDING_CURRENT_LOGS, // Sending ongoing logs as they acrue. 86// 87// In more detail, we have: 88// 89// INITIALIZED, // Constructor was called. 90// The MS has been constructed, but has taken no actions to compose the 91// initial log. 92// 93// INIT_TASK_SCHEDULED, // Waiting for deferred init tasks to finish. 94// Typically about 30 seconds after startup, a task is sent to a second thread 95// (the file thread) to perform deferred (lower priority and slower) 96// initialization steps such as getting the list of plugins. That task will 97// (when complete) make an async callback (via a Task) to indicate the 98// completion. 99// 100// INIT_TASK_DONE, // Waiting for timer to send initial log. 101// The callback has arrived, and it is now possible for an initial log to be 102// created. This callback typically arrives back less than one second after 103// the deferred init task is dispatched. 104// 105// SENDING_INITIAL_STABILITY_LOG, // Initial stability log being sent. 106// During initialization, if a crash occurred during the previous session, an 107// initial stability log will be generated and registered with the log manager. 108// This state will be entered if a stability log was prepared during metrics 109// service initialization (in InitializeMetricsRecordingState()) and is waiting 110// to be transmitted when it's time to send up the first log (per the reporting 111// scheduler). If there is no initial stability log (e.g. there was no previous 112// crash), then this state will be skipped and the state will advance to 113// SENDING_INITIAL_METRICS_LOG. 114// 115// SENDING_INITIAL_METRICS_LOG, // Initial metrics log being sent. 116// This state is entered after the initial metrics log has been composed, and 117// prepared for transmission. This happens after SENDING_INITIAL_STABILITY_LOG 118// if there was an initial stability log (see above). It is also the case that 119// any previously unsent logs have been loaded into instance variables for 120// possible transmission. 121// 122// SENDING_OLD_LOGS, // Sending unsent logs from previous session. 123// This state indicates that the initial log for this session has been 124// successfully sent and it is now time to send any logs that were 125// saved from previous sessions. All such logs will be transmitted before 126// exiting this state, and proceeding with ongoing logs from the current session 127// (see next state). 128// 129// SENDING_CURRENT_LOGS, // Sending standard current logs as they accrue. 130// Current logs are being accumulated. Typically every 20 minutes a log is 131// closed and finalized for transmission, at the same time as a new log is 132// started. 133// 134// The progression through the above states is simple, and sequential, in the 135// most common use cases. States proceed from INITIAL to SENDING_CURRENT_LOGS, 136// and remain in the latter until shutdown. 137// 138// The one unusual case is when the user asks that we stop logging. When that 139// happens, any staged (transmission in progress) log is persisted, and any log 140// that is currently accumulating is also finalized and persisted. We then 141// regress back to the SEND_OLD_LOGS state in case the user enables log 142// recording again during this session. This way anything we have persisted 143// will be sent automatically if/when we progress back to SENDING_CURRENT_LOG 144// state. 145// 146// Another similar case is on mobile, when the application is backgrounded and 147// then foregrounded again. Backgrounding created new "old" stored logs, so the 148// state drops back from SENDING_CURRENT_LOGS to SENDING_OLD_LOGS so those logs 149// will be sent. 150// 151// Also note that whenever we successfully send an old log, we mirror the list 152// of logs into the PrefService. This ensures that IF we crash, we won't start 153// up and retransmit our old logs again. 154// 155// Due to race conditions, it is always possible that a log file could be sent 156// twice. For example, if a log file is sent, but not yet acknowledged by 157// the external server, and the user shuts down, then a copy of the log may be 158// saved for re-transmission. These duplicates could be filtered out server 159// side, but are not expected to be a significant problem. 160// 161// 162//------------------------------------------------------------------------------ 163 164#include "components/metrics/metrics_service.h" 165 166#include <algorithm> 167 168#include "base/bind.h" 169#include "base/callback.h" 170#include "base/metrics/histogram.h" 171#include "base/metrics/histogram_base.h" 172#include "base/metrics/histogram_samples.h" 173#include "base/metrics/sparse_histogram.h" 174#include "base/metrics/statistics_recorder.h" 175#include "base/prefs/pref_registry_simple.h" 176#include "base/prefs/pref_service.h" 177#include "base/strings/string_number_conversions.h" 178#include "base/strings/utf_string_conversions.h" 179#include "base/threading/platform_thread.h" 180#include "base/threading/thread.h" 181#include "base/threading/thread_restrictions.h" 182#include "base/tracked_objects.h" 183#include "base/values.h" 184#include "components/metrics/metrics_log.h" 185#include "components/metrics/metrics_log_manager.h" 186#include "components/metrics/metrics_log_uploader.h" 187#include "components/metrics/metrics_pref_names.h" 188#include "components/metrics/metrics_reporting_scheduler.h" 189#include "components/metrics/metrics_service_client.h" 190#include "components/metrics/metrics_state_manager.h" 191#include "components/variations/entropy_provider.h" 192 193using base::Time; 194using metrics::MetricsLogManager; 195 196namespace { 197 198// Check to see that we're being called on only one thread. 199bool IsSingleThreaded() { 200 static base::PlatformThreadId thread_id = 0; 201 if (!thread_id) 202 thread_id = base::PlatformThread::CurrentId(); 203 return base::PlatformThread::CurrentId() == thread_id; 204} 205 206// The delay, in seconds, after starting recording before doing expensive 207// initialization work. 208#if defined(OS_ANDROID) || defined(OS_IOS) 209// On mobile devices, a significant portion of sessions last less than a minute. 210// Use a shorter timer on these platforms to avoid losing data. 211// TODO(dfalcantara): To avoid delaying startup, tighten up initialization so 212// that it occurs after the user gets their initial page. 213const int kInitializationDelaySeconds = 5; 214#else 215const int kInitializationDelaySeconds = 30; 216#endif 217 218// The maximum number of events in a log uploaded to the UMA server. 219const int kEventLimit = 2400; 220 221// If an upload fails, and the transmission was over this byte count, then we 222// will discard the log, and not try to retransmit it. We also don't persist 223// the log to the prefs for transmission during the next chrome session if this 224// limit is exceeded. 225const size_t kUploadLogAvoidRetransmitSize = 100 * 1024; 226 227// Interval, in minutes, between state saves. 228const int kSaveStateIntervalMinutes = 5; 229 230// The metrics server's URL. 231const char kServerUrl[] = "https://clients4.google.com/uma/v2"; 232 233// The MIME type for the uploaded metrics data. 234const char kMimeType[] = "application/vnd.chrome.uma"; 235 236enum ResponseStatus { 237 UNKNOWN_FAILURE, 238 SUCCESS, 239 BAD_REQUEST, // Invalid syntax or log too large. 240 NO_RESPONSE, 241 NUM_RESPONSE_STATUSES 242}; 243 244ResponseStatus ResponseCodeToStatus(int response_code) { 245 switch (response_code) { 246 case -1: 247 return NO_RESPONSE; 248 case 200: 249 return SUCCESS; 250 case 400: 251 return BAD_REQUEST; 252 default: 253 return UNKNOWN_FAILURE; 254 } 255} 256 257void MarkAppCleanShutdownAndCommit(PrefService* local_state) { 258 local_state->SetBoolean(metrics::prefs::kStabilityExitedCleanly, true); 259 local_state->SetInteger(metrics::prefs::kStabilityExecutionPhase, 260 MetricsService::SHUTDOWN_COMPLETE); 261 // Start writing right away (write happens on a different thread). 262 local_state->CommitPendingWrite(); 263} 264 265} // namespace 266 267 268SyntheticTrialGroup::SyntheticTrialGroup(uint32 trial, uint32 group) { 269 id.name = trial; 270 id.group = group; 271} 272 273SyntheticTrialGroup::~SyntheticTrialGroup() { 274} 275 276// static 277MetricsService::ShutdownCleanliness MetricsService::clean_shutdown_status_ = 278 MetricsService::CLEANLY_SHUTDOWN; 279 280MetricsService::ExecutionPhase MetricsService::execution_phase_ = 281 MetricsService::UNINITIALIZED_PHASE; 282 283// static 284void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) { 285 DCHECK(IsSingleThreaded()); 286 metrics::MetricsStateManager::RegisterPrefs(registry); 287 MetricsLog::RegisterPrefs(registry); 288 289 registry->RegisterInt64Pref(metrics::prefs::kStabilityLaunchTimeSec, 0); 290 registry->RegisterInt64Pref(metrics::prefs::kStabilityLastTimestampSec, 0); 291 registry->RegisterStringPref(metrics::prefs::kStabilityStatsVersion, 292 std::string()); 293 registry->RegisterInt64Pref(metrics::prefs::kStabilityStatsBuildTime, 0); 294 registry->RegisterBooleanPref(metrics::prefs::kStabilityExitedCleanly, true); 295 registry->RegisterIntegerPref(metrics::prefs::kStabilityExecutionPhase, 296 UNINITIALIZED_PHASE); 297 registry->RegisterBooleanPref(metrics::prefs::kStabilitySessionEndCompleted, 298 true); 299 registry->RegisterIntegerPref(metrics::prefs::kMetricsSessionID, -1); 300 301 registry->RegisterListPref(metrics::prefs::kMetricsInitialLogs); 302 registry->RegisterListPref(metrics::prefs::kMetricsOngoingLogs); 303 registry->RegisterListPref(metrics::prefs::kMetricsInitialLogsOld); 304 registry->RegisterListPref(metrics::prefs::kMetricsOngoingLogsOld); 305 306 registry->RegisterInt64Pref(metrics::prefs::kUninstallLaunchCount, 0); 307 registry->RegisterInt64Pref(metrics::prefs::kUninstallMetricsUptimeSec, 0); 308} 309 310MetricsService::MetricsService(metrics::MetricsStateManager* state_manager, 311 metrics::MetricsServiceClient* client, 312 PrefService* local_state) 313 : log_manager_(local_state, kUploadLogAvoidRetransmitSize), 314 histogram_snapshot_manager_(this), 315 state_manager_(state_manager), 316 client_(client), 317 local_state_(local_state), 318 recording_active_(false), 319 reporting_active_(false), 320 test_mode_active_(false), 321 state_(INITIALIZED), 322 has_initial_stability_log_(false), 323 log_upload_in_progress_(false), 324 idle_since_last_transmission_(false), 325 session_id_(-1), 326 self_ptr_factory_(this), 327 state_saver_factory_(this) { 328 DCHECK(IsSingleThreaded()); 329 DCHECK(state_manager_); 330 DCHECK(client_); 331 DCHECK(local_state_); 332} 333 334MetricsService::~MetricsService() { 335 DisableRecording(); 336} 337 338void MetricsService::InitializeMetricsRecordingState() { 339 InitializeMetricsState(); 340 341 base::Closure callback = base::Bind(&MetricsService::StartScheduledUpload, 342 self_ptr_factory_.GetWeakPtr()); 343 scheduler_.reset(new MetricsReportingScheduler(callback)); 344} 345 346void MetricsService::Start() { 347 HandleIdleSinceLastTransmission(false); 348 EnableRecording(); 349 EnableReporting(); 350} 351 352bool MetricsService::StartIfMetricsReportingEnabled() { 353 const bool enabled = state_manager_->IsMetricsReportingEnabled(); 354 if (enabled) 355 Start(); 356 return enabled; 357} 358 359void MetricsService::StartRecordingForTests() { 360 test_mode_active_ = true; 361 EnableRecording(); 362 DisableReporting(); 363} 364 365void MetricsService::Stop() { 366 HandleIdleSinceLastTransmission(false); 367 DisableReporting(); 368 DisableRecording(); 369} 370 371void MetricsService::EnableReporting() { 372 if (reporting_active_) 373 return; 374 reporting_active_ = true; 375 StartSchedulerIfNecessary(); 376} 377 378void MetricsService::DisableReporting() { 379 reporting_active_ = false; 380} 381 382std::string MetricsService::GetClientId() { 383 return state_manager_->client_id(); 384} 385 386scoped_ptr<const base::FieldTrial::EntropyProvider> 387MetricsService::CreateEntropyProvider() { 388 // TODO(asvitkine): Refactor the code so that MetricsService does not expose 389 // this method. 390 return state_manager_->CreateEntropyProvider(); 391} 392 393void MetricsService::EnableRecording() { 394 DCHECK(IsSingleThreaded()); 395 396 if (recording_active_) 397 return; 398 recording_active_ = true; 399 400 state_manager_->ForceClientIdCreation(); 401 client_->SetClientID(state_manager_->client_id()); 402 if (!log_manager_.current_log()) 403 OpenNewLog(); 404 405 for (size_t i = 0; i < metrics_providers_.size(); ++i) 406 metrics_providers_[i]->OnRecordingEnabled(); 407 408 base::RemoveActionCallback(action_callback_); 409 action_callback_ = base::Bind(&MetricsService::OnUserAction, 410 base::Unretained(this)); 411 base::AddActionCallback(action_callback_); 412} 413 414void MetricsService::DisableRecording() { 415 DCHECK(IsSingleThreaded()); 416 417 if (!recording_active_) 418 return; 419 recording_active_ = false; 420 421 base::RemoveActionCallback(action_callback_); 422 423 for (size_t i = 0; i < metrics_providers_.size(); ++i) 424 metrics_providers_[i]->OnRecordingDisabled(); 425 426 PushPendingLogsToPersistentStorage(); 427 DCHECK(!log_manager_.has_staged_log()); 428} 429 430bool MetricsService::recording_active() const { 431 DCHECK(IsSingleThreaded()); 432 return recording_active_; 433} 434 435bool MetricsService::reporting_active() const { 436 DCHECK(IsSingleThreaded()); 437 return reporting_active_; 438} 439 440void MetricsService::RecordDelta(const base::HistogramBase& histogram, 441 const base::HistogramSamples& snapshot) { 442 log_manager_.current_log()->RecordHistogramDelta(histogram.histogram_name(), 443 snapshot); 444} 445 446void MetricsService::InconsistencyDetected( 447 base::HistogramBase::Inconsistency problem) { 448 UMA_HISTOGRAM_ENUMERATION("Histogram.InconsistenciesBrowser", 449 problem, base::HistogramBase::NEVER_EXCEEDED_VALUE); 450} 451 452void MetricsService::UniqueInconsistencyDetected( 453 base::HistogramBase::Inconsistency problem) { 454 UMA_HISTOGRAM_ENUMERATION("Histogram.InconsistenciesBrowserUnique", 455 problem, base::HistogramBase::NEVER_EXCEEDED_VALUE); 456} 457 458void MetricsService::InconsistencyDetectedInLoggedCount(int amount) { 459 UMA_HISTOGRAM_COUNTS("Histogram.InconsistentSnapshotBrowser", 460 std::abs(amount)); 461} 462 463void MetricsService::HandleIdleSinceLastTransmission(bool in_idle) { 464 // If there wasn't a lot of action, maybe the computer was asleep, in which 465 // case, the log transmissions should have stopped. Here we start them up 466 // again. 467 if (!in_idle && idle_since_last_transmission_) 468 StartSchedulerIfNecessary(); 469 idle_since_last_transmission_ = in_idle; 470} 471 472void MetricsService::OnApplicationNotIdle() { 473 if (recording_active_) 474 HandleIdleSinceLastTransmission(false); 475} 476 477void MetricsService::RecordStartOfSessionEnd() { 478 LogCleanShutdown(); 479 RecordBooleanPrefValue(metrics::prefs::kStabilitySessionEndCompleted, false); 480} 481 482void MetricsService::RecordCompletedSessionEnd() { 483 LogCleanShutdown(); 484 RecordBooleanPrefValue(metrics::prefs::kStabilitySessionEndCompleted, true); 485} 486 487#if defined(OS_ANDROID) || defined(OS_IOS) 488void MetricsService::OnAppEnterBackground() { 489 scheduler_->Stop(); 490 491 MarkAppCleanShutdownAndCommit(local_state_); 492 493 // At this point, there's no way of knowing when the process will be 494 // killed, so this has to be treated similar to a shutdown, closing and 495 // persisting all logs. Unlinke a shutdown, the state is primed to be ready 496 // to continue logging and uploading if the process does return. 497 if (recording_active() && state_ >= SENDING_INITIAL_STABILITY_LOG) { 498 PushPendingLogsToPersistentStorage(); 499 // Persisting logs closes the current log, so start recording a new log 500 // immediately to capture any background work that might be done before the 501 // process is killed. 502 OpenNewLog(); 503 } 504} 505 506void MetricsService::OnAppEnterForeground() { 507 local_state_->SetBoolean(metrics::prefs::kStabilityExitedCleanly, false); 508 StartSchedulerIfNecessary(); 509} 510#else 511void MetricsService::LogNeedForCleanShutdown(PrefService* local_state) { 512 local_state->SetBoolean(metrics::prefs::kStabilityExitedCleanly, false); 513 // Redundant setting to be sure we call for a clean shutdown. 514 clean_shutdown_status_ = NEED_TO_SHUTDOWN; 515} 516#endif // defined(OS_ANDROID) || defined(OS_IOS) 517 518// static 519void MetricsService::SetExecutionPhase(ExecutionPhase execution_phase, 520 PrefService* local_state) { 521 execution_phase_ = execution_phase; 522 local_state->SetInteger(metrics::prefs::kStabilityExecutionPhase, 523 execution_phase_); 524} 525 526void MetricsService::RecordBreakpadRegistration(bool success) { 527 if (!success) 528 IncrementPrefValue(metrics::prefs::kStabilityBreakpadRegistrationFail); 529 else 530 IncrementPrefValue(metrics::prefs::kStabilityBreakpadRegistrationSuccess); 531} 532 533void MetricsService::RecordBreakpadHasDebugger(bool has_debugger) { 534 if (!has_debugger) 535 IncrementPrefValue(metrics::prefs::kStabilityDebuggerNotPresent); 536 else 537 IncrementPrefValue(metrics::prefs::kStabilityDebuggerPresent); 538} 539 540//------------------------------------------------------------------------------ 541// private methods 542//------------------------------------------------------------------------------ 543 544 545//------------------------------------------------------------------------------ 546// Initialization methods 547 548void MetricsService::InitializeMetricsState() { 549 local_state_->SetString(metrics::prefs::kStabilityStatsVersion, 550 client_->GetVersionString()); 551 local_state_->SetInt64(metrics::prefs::kStabilityStatsBuildTime, 552 MetricsLog::GetBuildTime()); 553 554 session_id_ = local_state_->GetInteger(metrics::prefs::kMetricsSessionID); 555 556 if (!local_state_->GetBoolean(metrics::prefs::kStabilityExitedCleanly)) { 557 IncrementPrefValue(metrics::prefs::kStabilityCrashCount); 558 // Reset flag, and wait until we call LogNeedForCleanShutdown() before 559 // monitoring. 560 local_state_->SetBoolean(metrics::prefs::kStabilityExitedCleanly, true); 561 562 // TODO(rtenneti): On windows, consider saving/getting execution_phase from 563 // the registry. 564 int execution_phase = 565 local_state_->GetInteger(metrics::prefs::kStabilityExecutionPhase); 566 UMA_HISTOGRAM_SPARSE_SLOWLY("Chrome.Browser.CrashedExecutionPhase", 567 execution_phase); 568 569 // If the previous session didn't exit cleanly, then prepare an initial 570 // stability log if UMA is enabled. 571 if (state_manager_->IsMetricsReportingEnabled()) 572 PrepareInitialStabilityLog(); 573 } 574 575 // Update session ID. 576 ++session_id_; 577 local_state_->SetInteger(metrics::prefs::kMetricsSessionID, session_id_); 578 579 // Stability bookkeeping 580 IncrementPrefValue(metrics::prefs::kStabilityLaunchCount); 581 582 DCHECK_EQ(UNINITIALIZED_PHASE, execution_phase_); 583 SetExecutionPhase(START_METRICS_RECORDING, local_state_); 584 585 if (!local_state_->GetBoolean( 586 metrics::prefs::kStabilitySessionEndCompleted)) { 587 IncrementPrefValue(metrics::prefs::kStabilityIncompleteSessionEndCount); 588 // This is marked false when we get a WM_ENDSESSION. 589 local_state_->SetBoolean(metrics::prefs::kStabilitySessionEndCompleted, 590 true); 591 } 592 593 // Call GetUptimes() for the first time, thus allowing all later calls 594 // to record incremental uptimes accurately. 595 base::TimeDelta ignored_uptime_parameter; 596 base::TimeDelta startup_uptime; 597 GetUptimes(local_state_, &startup_uptime, &ignored_uptime_parameter); 598 DCHECK_EQ(0, startup_uptime.InMicroseconds()); 599 // For backwards compatibility, leave this intact in case Omaha is checking 600 // them. metrics::prefs::kStabilityLastTimestampSec may also be useless now. 601 // TODO(jar): Delete these if they have no uses. 602 local_state_->SetInt64(metrics::prefs::kStabilityLaunchTimeSec, 603 Time::Now().ToTimeT()); 604 605 // Bookkeeping for the uninstall metrics. 606 IncrementLongPrefsValue(metrics::prefs::kUninstallLaunchCount); 607 608 // Kick off the process of saving the state (so the uptime numbers keep 609 // getting updated) every n minutes. 610 ScheduleNextStateSave(); 611} 612 613void MetricsService::OnUserAction(const std::string& action) { 614 if (!ShouldLogEvents()) 615 return; 616 617 log_manager_.current_log()->RecordUserAction(action); 618 HandleIdleSinceLastTransmission(false); 619} 620 621void MetricsService::FinishedGatheringInitialMetrics() { 622 DCHECK_EQ(INIT_TASK_SCHEDULED, state_); 623 state_ = INIT_TASK_DONE; 624 625 // Create the initial log. 626 if (!initial_metrics_log_.get()) { 627 initial_metrics_log_ = CreateLog(MetricsLog::ONGOING_LOG); 628 NotifyOnDidCreateMetricsLog(); 629 } 630 631 scheduler_->InitTaskComplete(); 632} 633 634void MetricsService::GetUptimes(PrefService* pref, 635 base::TimeDelta* incremental_uptime, 636 base::TimeDelta* uptime) { 637 base::TimeTicks now = base::TimeTicks::Now(); 638 // If this is the first call, init |first_updated_time_| and 639 // |last_updated_time_|. 640 if (last_updated_time_.is_null()) { 641 first_updated_time_ = now; 642 last_updated_time_ = now; 643 } 644 *incremental_uptime = now - last_updated_time_; 645 *uptime = now - first_updated_time_; 646 last_updated_time_ = now; 647 648 const int64 incremental_time_secs = incremental_uptime->InSeconds(); 649 if (incremental_time_secs > 0) { 650 int64 metrics_uptime = 651 pref->GetInt64(metrics::prefs::kUninstallMetricsUptimeSec); 652 metrics_uptime += incremental_time_secs; 653 pref->SetInt64(metrics::prefs::kUninstallMetricsUptimeSec, metrics_uptime); 654 } 655} 656 657void MetricsService::AddObserver(MetricsServiceObserver* observer) { 658 DCHECK(thread_checker_.CalledOnValidThread()); 659 observers_.AddObserver(observer); 660} 661 662void MetricsService::RemoveObserver(MetricsServiceObserver* observer) { 663 DCHECK(thread_checker_.CalledOnValidThread()); 664 observers_.RemoveObserver(observer); 665} 666 667void MetricsService::NotifyOnDidCreateMetricsLog() { 668 DCHECK(thread_checker_.CalledOnValidThread()); 669 FOR_EACH_OBSERVER( 670 MetricsServiceObserver, observers_, OnDidCreateMetricsLog()); 671 for (size_t i = 0; i < metrics_providers_.size(); ++i) 672 metrics_providers_[i]->OnDidCreateMetricsLog(); 673} 674 675//------------------------------------------------------------------------------ 676// State save methods 677 678void MetricsService::ScheduleNextStateSave() { 679 state_saver_factory_.InvalidateWeakPtrs(); 680 681 base::MessageLoop::current()->PostDelayedTask(FROM_HERE, 682 base::Bind(&MetricsService::SaveLocalState, 683 state_saver_factory_.GetWeakPtr()), 684 base::TimeDelta::FromMinutes(kSaveStateIntervalMinutes)); 685} 686 687void MetricsService::SaveLocalState() { 688 RecordCurrentState(local_state_); 689 690 // TODO(jar):110021 Does this run down the batteries???? 691 ScheduleNextStateSave(); 692} 693 694 695//------------------------------------------------------------------------------ 696// Recording control methods 697 698void MetricsService::OpenNewLog() { 699 DCHECK(!log_manager_.current_log()); 700 701 log_manager_.BeginLoggingWithLog(CreateLog(MetricsLog::ONGOING_LOG)); 702 NotifyOnDidCreateMetricsLog(); 703 if (state_ == INITIALIZED) { 704 // We only need to schedule that run once. 705 state_ = INIT_TASK_SCHEDULED; 706 707 base::MessageLoop::current()->PostDelayedTask( 708 FROM_HERE, 709 base::Bind(&MetricsService::StartGatheringMetrics, 710 self_ptr_factory_.GetWeakPtr()), 711 base::TimeDelta::FromSeconds(kInitializationDelaySeconds)); 712 } 713} 714 715void MetricsService::StartGatheringMetrics() { 716 client_->StartGatheringMetrics( 717 base::Bind(&MetricsService::FinishedGatheringInitialMetrics, 718 self_ptr_factory_.GetWeakPtr())); 719} 720 721void MetricsService::CloseCurrentLog() { 722 if (!log_manager_.current_log()) 723 return; 724 725 // TODO(jar): Integrate bounds on log recording more consistently, so that we 726 // can stop recording logs that are too big much sooner. 727 if (log_manager_.current_log()->num_events() > kEventLimit) { 728 UMA_HISTOGRAM_COUNTS("UMA.Discarded Log Events", 729 log_manager_.current_log()->num_events()); 730 log_manager_.DiscardCurrentLog(); 731 OpenNewLog(); // Start trivial log to hold our histograms. 732 } 733 734 // Put incremental data (histogram deltas, and realtime stats deltas) at the 735 // end of all log transmissions (initial log handles this separately). 736 // RecordIncrementalStabilityElements only exists on the derived 737 // MetricsLog class. 738 MetricsLog* current_log = 739 static_cast<MetricsLog*>(log_manager_.current_log()); 740 DCHECK(current_log); 741 std::vector<variations::ActiveGroupId> synthetic_trials; 742 GetCurrentSyntheticFieldTrials(&synthetic_trials); 743 current_log->RecordEnvironment(metrics_providers_.get(), synthetic_trials); 744 base::TimeDelta incremental_uptime; 745 base::TimeDelta uptime; 746 GetUptimes(local_state_, &incremental_uptime, &uptime); 747 current_log->RecordStabilityMetrics(metrics_providers_.get(), 748 incremental_uptime, uptime); 749 750 RecordCurrentHistograms(); 751 current_log->RecordGeneralMetrics(metrics_providers_.get()); 752 753 log_manager_.FinishCurrentLog(); 754} 755 756void MetricsService::PushPendingLogsToPersistentStorage() { 757 if (state_ < SENDING_INITIAL_STABILITY_LOG) 758 return; // We didn't and still don't have time to get plugin list etc. 759 760 if (log_manager_.has_staged_log()) { 761 // We may race here, and send second copy of the log later. 762 metrics::PersistedLogs::StoreType store_type; 763 if (log_upload_in_progress_) 764 store_type = metrics::PersistedLogs::PROVISIONAL_STORE; 765 else 766 store_type = metrics::PersistedLogs::NORMAL_STORE; 767 log_manager_.StoreStagedLogAsUnsent(store_type); 768 } 769 DCHECK(!log_manager_.has_staged_log()); 770 CloseCurrentLog(); 771 log_manager_.PersistUnsentLogs(); 772 773 // If there was a staged and/or current log, then there is now at least one 774 // log waiting to be uploaded. 775 if (log_manager_.has_unsent_logs()) 776 state_ = SENDING_OLD_LOGS; 777} 778 779//------------------------------------------------------------------------------ 780// Transmission of logs methods 781 782void MetricsService::StartSchedulerIfNecessary() { 783 // Never schedule cutting or uploading of logs in test mode. 784 if (test_mode_active_) 785 return; 786 787 // Even if reporting is disabled, the scheduler is needed to trigger the 788 // creation of the initial log, which must be done in order for any logs to be 789 // persisted on shutdown or backgrounding. 790 if (recording_active() && 791 (reporting_active() || state_ < SENDING_INITIAL_STABILITY_LOG)) { 792 scheduler_->Start(); 793 } 794} 795 796void MetricsService::StartScheduledUpload() { 797 // If we're getting no notifications, then the log won't have much in it, and 798 // it's possible the computer is about to go to sleep, so don't upload and 799 // stop the scheduler. 800 // If recording has been turned off, the scheduler doesn't need to run. 801 // If reporting is off, proceed if the initial log hasn't been created, since 802 // that has to happen in order for logs to be cut and stored when persisting. 803 // TODO(stuartmorgan): Call Stop() on the scheduler when reporting and/or 804 // recording are turned off instead of letting it fire and then aborting. 805 if (idle_since_last_transmission_ || 806 !recording_active() || 807 (!reporting_active() && state_ >= SENDING_INITIAL_STABILITY_LOG)) { 808 scheduler_->Stop(); 809 scheduler_->UploadCancelled(); 810 return; 811 } 812 813 // If the callback was to upload an old log, but there no longer is one, 814 // just report success back to the scheduler to begin the ongoing log 815 // callbacks. 816 // TODO(stuartmorgan): Consider removing the distinction between 817 // SENDING_OLD_LOGS and SENDING_CURRENT_LOGS to simplify the state machine 818 // now that the log upload flow is the same for both modes. 819 if (state_ == SENDING_OLD_LOGS && !log_manager_.has_unsent_logs()) { 820 state_ = SENDING_CURRENT_LOGS; 821 scheduler_->UploadFinished(true /* healthy */, false /* no unsent logs */); 822 return; 823 } 824 // If there are unsent logs, send the next one. If not, start the asynchronous 825 // process of finalizing the current log for upload. 826 if (state_ == SENDING_OLD_LOGS) { 827 DCHECK(log_manager_.has_unsent_logs()); 828 log_manager_.StageNextLogForUpload(); 829 SendStagedLog(); 830 } else { 831 client_->CollectFinalMetrics( 832 base::Bind(&MetricsService::OnFinalLogInfoCollectionDone, 833 self_ptr_factory_.GetWeakPtr())); 834 } 835} 836 837void MetricsService::OnFinalLogInfoCollectionDone() { 838 // If somehow there is a log upload in progress, we return and hope things 839 // work out. The scheduler isn't informed since if this happens, the scheduler 840 // will get a response from the upload. 841 DCHECK(!log_upload_in_progress_); 842 if (log_upload_in_progress_) 843 return; 844 845 // Abort if metrics were turned off during the final info gathering. 846 if (!recording_active()) { 847 scheduler_->Stop(); 848 scheduler_->UploadCancelled(); 849 return; 850 } 851 852 StageNewLog(); 853 854 // If logs shouldn't be uploaded, stop here. It's important that this check 855 // be after StageNewLog(), otherwise the previous logs will never be loaded, 856 // and thus the open log won't be persisted. 857 // TODO(stuartmorgan): This is unnecessarily complicated; restructure loading 858 // of previous logs to not require running part of the upload logic. 859 // http://crbug.com/157337 860 if (!reporting_active()) { 861 scheduler_->Stop(); 862 scheduler_->UploadCancelled(); 863 return; 864 } 865 866 SendStagedLog(); 867} 868 869void MetricsService::StageNewLog() { 870 if (log_manager_.has_staged_log()) 871 return; 872 873 switch (state_) { 874 case INITIALIZED: 875 case INIT_TASK_SCHEDULED: // We should be further along by now. 876 NOTREACHED(); 877 return; 878 879 case INIT_TASK_DONE: 880 if (has_initial_stability_log_) { 881 // There's an initial stability log, ready to send. 882 log_manager_.StageNextLogForUpload(); 883 has_initial_stability_log_ = false; 884 // Note: No need to call LoadPersistedUnsentLogs() here because unsent 885 // logs have already been loaded by PrepareInitialStabilityLog(). 886 state_ = SENDING_INITIAL_STABILITY_LOG; 887 } else { 888 PrepareInitialMetricsLog(); 889 // Load unsent logs (if any) from local state. 890 log_manager_.LoadPersistedUnsentLogs(); 891 state_ = SENDING_INITIAL_METRICS_LOG; 892 } 893 break; 894 895 case SENDING_OLD_LOGS: 896 NOTREACHED(); // Shouldn't be staging a new log during old log sending. 897 return; 898 899 case SENDING_CURRENT_LOGS: 900 CloseCurrentLog(); 901 OpenNewLog(); 902 log_manager_.StageNextLogForUpload(); 903 break; 904 905 default: 906 NOTREACHED(); 907 return; 908 } 909 910 DCHECK(log_manager_.has_staged_log()); 911} 912 913void MetricsService::PrepareInitialStabilityLog() { 914 DCHECK_EQ(INITIALIZED, state_); 915 DCHECK_NE(0, local_state_->GetInteger(metrics::prefs::kStabilityCrashCount)); 916 917 scoped_ptr<MetricsLog> initial_stability_log( 918 CreateLog(MetricsLog::INITIAL_STABILITY_LOG)); 919 920 // Do not call NotifyOnDidCreateMetricsLog here because the stability 921 // log describes stats from the _previous_ session. 922 923 if (!initial_stability_log->LoadSavedEnvironmentFromPrefs()) 924 return; 925 926 log_manager_.LoadPersistedUnsentLogs(); 927 928 log_manager_.PauseCurrentLog(); 929 log_manager_.BeginLoggingWithLog(initial_stability_log.Pass()); 930 931 // Note: Some stability providers may record stability stats via histograms, 932 // so this call has to be after BeginLoggingWithLog(). 933 log_manager_.current_log()->RecordStabilityMetrics( 934 metrics_providers_.get(), base::TimeDelta(), base::TimeDelta()); 935 RecordCurrentStabilityHistograms(); 936 937 // Note: RecordGeneralMetrics() intentionally not called since this log is for 938 // stability stats from a previous session only. 939 940 log_manager_.FinishCurrentLog(); 941 log_manager_.ResumePausedLog(); 942 943 // Store unsent logs, including the stability log that was just saved, so 944 // that they're not lost in case of a crash before upload time. 945 log_manager_.PersistUnsentLogs(); 946 947 has_initial_stability_log_ = true; 948} 949 950void MetricsService::PrepareInitialMetricsLog() { 951 DCHECK(state_ == INIT_TASK_DONE || state_ == SENDING_INITIAL_STABILITY_LOG); 952 953 std::vector<variations::ActiveGroupId> synthetic_trials; 954 GetCurrentSyntheticFieldTrials(&synthetic_trials); 955 initial_metrics_log_->RecordEnvironment(metrics_providers_.get(), 956 synthetic_trials); 957 base::TimeDelta incremental_uptime; 958 base::TimeDelta uptime; 959 GetUptimes(local_state_, &incremental_uptime, &uptime); 960 961 // Histograms only get written to the current log, so make the new log current 962 // before writing them. 963 log_manager_.PauseCurrentLog(); 964 log_manager_.BeginLoggingWithLog(initial_metrics_log_.Pass()); 965 966 // Note: Some stability providers may record stability stats via histograms, 967 // so this call has to be after BeginLoggingWithLog(). 968 MetricsLog* current_log = 969 static_cast<MetricsLog*>(log_manager_.current_log()); 970 current_log->RecordStabilityMetrics(metrics_providers_.get(), 971 base::TimeDelta(), base::TimeDelta()); 972 RecordCurrentHistograms(); 973 974 current_log->RecordGeneralMetrics(metrics_providers_.get()); 975 976 log_manager_.FinishCurrentLog(); 977 log_manager_.ResumePausedLog(); 978 979 DCHECK(!log_manager_.has_staged_log()); 980 log_manager_.StageNextLogForUpload(); 981} 982 983void MetricsService::SendStagedLog() { 984 DCHECK(log_manager_.has_staged_log()); 985 if (!log_manager_.has_staged_log()) 986 return; 987 988 DCHECK(!log_upload_in_progress_); 989 log_upload_in_progress_ = true; 990 991 if (!log_uploader_) { 992 log_uploader_ = client_->CreateUploader( 993 kServerUrl, kMimeType, 994 base::Bind(&MetricsService::OnLogUploadComplete, 995 self_ptr_factory_.GetWeakPtr())); 996 } 997 998 const std::string hash = 999 base::HexEncode(log_manager_.staged_log_hash().data(), 1000 log_manager_.staged_log_hash().size()); 1001 bool success = log_uploader_->UploadLog(log_manager_.staged_log(), hash); 1002 UMA_HISTOGRAM_BOOLEAN("UMA.UploadCreation", success); 1003 if (!success) { 1004 // Skip this upload and hope things work out next time. 1005 log_manager_.DiscardStagedLog(); 1006 scheduler_->UploadCancelled(); 1007 log_upload_in_progress_ = false; 1008 return; 1009 } 1010 1011 HandleIdleSinceLastTransmission(true); 1012} 1013 1014 1015void MetricsService::OnLogUploadComplete(int response_code) { 1016 DCHECK(log_upload_in_progress_); 1017 log_upload_in_progress_ = false; 1018 1019 // Log a histogram to track response success vs. failure rates. 1020 UMA_HISTOGRAM_ENUMERATION("UMA.UploadResponseStatus.Protobuf", 1021 ResponseCodeToStatus(response_code), 1022 NUM_RESPONSE_STATUSES); 1023 1024 // If the upload was provisionally stored, drop it now that the upload is 1025 // known to have gone through. 1026 log_manager_.DiscardLastProvisionalStore(); 1027 1028 bool upload_succeeded = response_code == 200; 1029 1030 // Provide boolean for error recovery (allow us to ignore response_code). 1031 bool discard_log = false; 1032 const size_t log_size = log_manager_.staged_log().length(); 1033 if (!upload_succeeded && log_size > kUploadLogAvoidRetransmitSize) { 1034 UMA_HISTOGRAM_COUNTS("UMA.Large Rejected Log was Discarded", 1035 static_cast<int>(log_size)); 1036 discard_log = true; 1037 } else if (response_code == 400) { 1038 // Bad syntax. Retransmission won't work. 1039 discard_log = true; 1040 } 1041 1042 if (upload_succeeded || discard_log) 1043 log_manager_.DiscardStagedLog(); 1044 1045 if (!log_manager_.has_staged_log()) { 1046 switch (state_) { 1047 case SENDING_INITIAL_STABILITY_LOG: 1048 // Store the updated list to disk now that the removed log is uploaded. 1049 log_manager_.PersistUnsentLogs(); 1050 PrepareInitialMetricsLog(); 1051 SendStagedLog(); 1052 state_ = SENDING_INITIAL_METRICS_LOG; 1053 break; 1054 1055 case SENDING_INITIAL_METRICS_LOG: 1056 // The initial metrics log never gets persisted to local state, so it's 1057 // not necessary to call log_manager_.PersistUnsentLogs() here. 1058 // TODO(asvitkine): It should be persisted like the initial stability 1059 // log and old unsent logs. http://crbug.com/328417 1060 state_ = log_manager_.has_unsent_logs() ? SENDING_OLD_LOGS 1061 : SENDING_CURRENT_LOGS; 1062 break; 1063 1064 case SENDING_OLD_LOGS: 1065 // Store the updated list to disk now that the removed log is uploaded. 1066 log_manager_.PersistUnsentLogs(); 1067 if (!log_manager_.has_unsent_logs()) 1068 state_ = SENDING_CURRENT_LOGS; 1069 break; 1070 1071 case SENDING_CURRENT_LOGS: 1072 break; 1073 1074 default: 1075 NOTREACHED(); 1076 break; 1077 } 1078 1079 if (log_manager_.has_unsent_logs()) 1080 DCHECK_LT(state_, SENDING_CURRENT_LOGS); 1081 } 1082 1083 // Error 400 indicates a problem with the log, not with the server, so 1084 // don't consider that a sign that the server is in trouble. 1085 bool server_is_healthy = upload_succeeded || response_code == 400; 1086 // Don't notify the scheduler that the upload is finished if we've only sent 1087 // the initial stability log, but not yet the initial metrics log (treat the 1088 // two as a single unit of work as far as the scheduler is concerned). 1089 if (state_ != SENDING_INITIAL_METRICS_LOG) { 1090 scheduler_->UploadFinished(server_is_healthy, 1091 log_manager_.has_unsent_logs()); 1092 } 1093 1094 if (server_is_healthy) 1095 client_->OnLogUploadComplete(); 1096} 1097 1098void MetricsService::IncrementPrefValue(const char* path) { 1099 int value = local_state_->GetInteger(path); 1100 local_state_->SetInteger(path, value + 1); 1101} 1102 1103void MetricsService::IncrementLongPrefsValue(const char* path) { 1104 int64 value = local_state_->GetInt64(path); 1105 local_state_->SetInt64(path, value + 1); 1106} 1107 1108bool MetricsService::UmaMetricsProperlyShutdown() { 1109 CHECK(clean_shutdown_status_ == CLEANLY_SHUTDOWN || 1110 clean_shutdown_status_ == NEED_TO_SHUTDOWN); 1111 return clean_shutdown_status_ == CLEANLY_SHUTDOWN; 1112} 1113 1114void MetricsService::RegisterSyntheticFieldTrial( 1115 const SyntheticTrialGroup& trial) { 1116 for (size_t i = 0; i < synthetic_trial_groups_.size(); ++i) { 1117 if (synthetic_trial_groups_[i].id.name == trial.id.name) { 1118 if (synthetic_trial_groups_[i].id.group != trial.id.group) { 1119 synthetic_trial_groups_[i].id.group = trial.id.group; 1120 synthetic_trial_groups_[i].start_time = base::TimeTicks::Now(); 1121 } 1122 return; 1123 } 1124 } 1125 1126 SyntheticTrialGroup trial_group = trial; 1127 trial_group.start_time = base::TimeTicks::Now(); 1128 synthetic_trial_groups_.push_back(trial_group); 1129} 1130 1131void MetricsService::RegisterMetricsProvider( 1132 scoped_ptr<metrics::MetricsProvider> provider) { 1133 DCHECK_EQ(INITIALIZED, state_); 1134 metrics_providers_.push_back(provider.release()); 1135} 1136 1137void MetricsService::CheckForClonedInstall( 1138 scoped_refptr<base::SingleThreadTaskRunner> task_runner) { 1139 state_manager_->CheckForClonedInstall(task_runner); 1140} 1141 1142void MetricsService::GetCurrentSyntheticFieldTrials( 1143 std::vector<variations::ActiveGroupId>* synthetic_trials) { 1144 DCHECK(synthetic_trials); 1145 synthetic_trials->clear(); 1146 const MetricsLog* current_log = 1147 static_cast<const MetricsLog*>(log_manager_.current_log()); 1148 for (size_t i = 0; i < synthetic_trial_groups_.size(); ++i) { 1149 if (synthetic_trial_groups_[i].start_time <= current_log->creation_time()) 1150 synthetic_trials->push_back(synthetic_trial_groups_[i].id); 1151 } 1152} 1153 1154scoped_ptr<MetricsLog> MetricsService::CreateLog(MetricsLog::LogType log_type) { 1155 return make_scoped_ptr(new MetricsLog(state_manager_->client_id(), 1156 session_id_, 1157 log_type, 1158 client_, 1159 local_state_)); 1160} 1161 1162void MetricsService::RecordCurrentHistograms() { 1163 DCHECK(log_manager_.current_log()); 1164 histogram_snapshot_manager_.PrepareDeltas( 1165 base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag); 1166} 1167 1168void MetricsService::RecordCurrentStabilityHistograms() { 1169 DCHECK(log_manager_.current_log()); 1170 histogram_snapshot_manager_.PrepareDeltas( 1171 base::Histogram::kNoFlags, base::Histogram::kUmaStabilityHistogramFlag); 1172} 1173 1174void MetricsService::LogCleanShutdown() { 1175 // Redundant hack to write pref ASAP. 1176 MarkAppCleanShutdownAndCommit(local_state_); 1177 1178 // Redundant setting to assure that we always reset this value at shutdown 1179 // (and that we don't use some alternate path, and not call LogCleanShutdown). 1180 clean_shutdown_status_ = CLEANLY_SHUTDOWN; 1181 1182 RecordBooleanPrefValue(metrics::prefs::kStabilityExitedCleanly, true); 1183 local_state_->SetInteger(metrics::prefs::kStabilityExecutionPhase, 1184 MetricsService::SHUTDOWN_COMPLETE); 1185} 1186 1187bool MetricsService::ShouldLogEvents() { 1188 // We simply don't log events to UMA if there is a single incognito 1189 // session visible. The problem is that we always notify using the orginal 1190 // profile in order to simplify notification processing. 1191 return !client_->IsOffTheRecordSessionActive(); 1192} 1193 1194void MetricsService::RecordBooleanPrefValue(const char* path, bool value) { 1195 DCHECK(IsSingleThreaded()); 1196 local_state_->SetBoolean(path, value); 1197 RecordCurrentState(local_state_); 1198} 1199 1200void MetricsService::RecordCurrentState(PrefService* pref) { 1201 pref->SetInt64(metrics::prefs::kStabilityLastTimestampSec, 1202 Time::Now().ToTimeT()); 1203 1204 for (size_t i = 0; i < metrics_providers_.size(); ++i) 1205 metrics_providers_[i]->RecordCurrentState(); 1206} 1207