blob: 8a560a6aa5963e87c4d93e5b6137a36d94ae8390 [file] [log] [blame]
// Copyright 2014 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.
#ifndef COMPONENTS_METRICS_CLEAN_EXIT_BEACON_H_
#define COMPONENTS_METRICS_CLEAN_EXIT_BEACON_H_
#include <string>
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/version_info/channel.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
class PrefRegistrySimple;
class PrefService;
namespace metrics {
// Captures all possible beacon value permutations for two distinct beacons.
// Exposed for testing.
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class CleanExitBeaconConsistency {
kCleanClean = 0,
kCleanDirty = 1,
kCleanMissing = 2,
kDirtyClean = 3,
kDirtyDirty = 4,
kDirtyMissing = 5,
kMissingClean = 6,
kMissingDirty = 7,
kMissingMissing = 8,
kMaxValue = kMissingMissing,
};
// Denotes the state of the beacon file. Exposed for testing.
//
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class BeaconFileState {
kReadable = 0,
kNotDeserializable = 1,
kMissingDictionary = 2,
kMissingCrashStreak = 3,
kMissingBeacon = 4,
kMaxValue = kMissingBeacon,
};
// Reads and updates a beacon used to detect whether the previous browser
// process exited cleanly.
class CleanExitBeacon {
public:
// Instantiates a CleanExitBeacon whose value is stored in |local_state|'s
// kStabilityExitedCleanly pref. |local_state| must be fully initialized.
//
// On Windows, |backup_registry_key| stores a backup of the beacon to verify
// that the pref's value corresponds to the registry's. |backup_registry_key|
// is ignored on other platforms, but iOS has a similar verification
// mechanism embedded inside CleanExitBeacon.
//
// |user_data_dir| is the path to the client's user data directory. If empty,
// a separate file will not be used for Variations Safe Mode prefs.
//
// TODO(crbug/1241702): Remove |channel| at the end of the Extended Variations
// Safe Mode experiment. |channel| is used to enable the experiment on only
// certain channels.
CleanExitBeacon(const std::wstring& backup_registry_key,
const base::FilePath& user_data_dir,
PrefService* local_state,
version_info::Channel channel);
virtual ~CleanExitBeacon() = default;
// Not copyable or movable.
CleanExitBeacon(const CleanExitBeacon&) = delete;
CleanExitBeacon& operator=(const CleanExitBeacon&) = delete;
// Initializes the CleanExitBeacon. This includes the following tasks:
// 1. Determining if the last session exited cleanly,
// 2. Incrementing the crash streak, if necessary, and
// 3. Emitting some metrics.
void Initialize();
// Returns the original value of the beacon.
bool exited_cleanly() const { return did_previous_session_exit_cleanly_; }
// Returns the original value of the last live timestamp.
base::Time browser_last_live_timestamp() const {
return initial_browser_last_live_timestamp_;
}
// Sets the beacon value to |exited_cleanly| and updates the last live
// timestamp. If |is_extended_safe_mode| is true, then the beacon value is
// written to disk synchronously. If false, a write is scheduled, and for
// clients in the Extended Variations Safe Mode experiment, a synchronous
// write is done, too.
//
// Note: |is_extended_safe_mode| should be true only for some clients in the
// Extended Variations Safe Mode experiment.
void WriteBeaconValue(bool exited_cleanly,
bool is_extended_safe_mode = false);
// Updates the last live timestamp.
void UpdateLastLiveTimestamp();
const base::FilePath GetUserDataDirForTesting() const;
base::FilePath GetBeaconFilePathForTesting() const;
// Registers local state prefs used by this class.
static void RegisterPrefs(PrefRegistrySimple* registry);
// Updates both Local State and NSUserDefaults beacon values.
static void SetStabilityExitedCleanlyForTesting(PrefService* local_state,
bool exited_cleanly);
// Resets both Local State and NSUserDefaults beacon values.
static void ResetStabilityExitedCleanlyForTesting(PrefService* local_state);
// CHECKs that Chrome exited cleanly.
static void EnsureCleanShutdown(PrefService* local_state);
#if BUILDFLAG(IS_IOS)
// Sets the NSUserDefaults beacon value.
static void SetUserDefaultsBeacon(bool exited_cleanly);
// Checks user default value of kUseUserDefaultsForExitedCleanlyBeacon.
// Because variations are not initialized early in startup, pair a user
// defaults value with the variations config.
static bool ShouldUseUserDefaultsBeacon();
// Syncs feature kUseUserDefaultsForExitedCleanlyBeacon to NSUserDefaults
// kUserDefaultsFeatureFlagForExitedCleanlyBeacon.
static void SyncUseUserDefaultsBeacon();
#endif // BUILDFLAG(IS_IOS)
// Prevents a test browser from performing two clean shutdown steps. First, it
// prevents the beacon value from being updated after this function is called.
// This prevents the the test browser from signaling that Chrome is shutting
// down cleanly. Second, it makes EnsureCleanShutdown() a no-op.
static void SkipCleanShutdownStepsForTesting();
private:
// Returns true if the previous session exited cleanly. Either Local State
// or |beacon_file_contents| is used to get this information. Which is used
// depends on the client's Extended Variations Safe Mode experiment group in
// the previous session. Also, records several metrics.
//
// Should be called only once: at startup.
//
// TODO(crbug/1241702): Update this comment when experimentation is over.
bool DidPreviousSessionExitCleanly(base::Value* beacon_file_contents);
// Writes |exited_cleanly| and the crash streak to the file located at
// |beacon_file_path_|.
void WriteBeaconFile(bool exited_cleanly) const;
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
// Returns whether Chrome exited cleanly in the previous session according to
// the platform-specific beacon (the registry for Windows or NSUserDefaults
// for iOS). Returns absl::nullopt if the platform-specific location does not
// have beacon info.
absl::optional<bool> ExitedCleanly();
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS)
#if BUILDFLAG(IS_IOS)
// Returns true if the NSUserDefaults beacon value is set.
static bool HasUserDefaultsBeacon();
// Returns the NSUserDefaults beacon value.
static bool GetUserDefaultsBeacon();
// Clears the NSUserDefaults beacon value.
static void ResetUserDefaultsBeacon();
#endif // BUILDFLAG(IS_IOS)
#if BUILDFLAG(IS_ANDROID)
// Denotes the time at which Chrome clients in the Extended Variations Safe
// Mode experiment's enabled group start watching for browser crashes.
base::TimeTicks extended_monitoring_stage_start_time_;
#endif
// Indicates whether the CleanExitBeacon has been initialized.
bool initialized_ = false;
// Stores a backup of the beacon. Windows only.
const std::wstring backup_registry_key_;
// Path to the client's user data directory. May be empty.
const base::FilePath user_data_dir_;
const raw_ptr<PrefService> local_state_;
// This is the value of the last live timestamp from local state at the time
// of construction. It is a timestamp from the previous browser session when
// the browser was known to be alive.
const base::Time initial_browser_last_live_timestamp_;
// The client's channel, e.g. Canary. Used to help determine whether the
// client should participate in the Extended Variations Safe Mode experiment.
// TODO(crbug/1241702): Remove at the end of the experiment.
[[maybe_unused]] const version_info::Channel channel_;
bool did_previous_session_exit_cleanly_ = false;
// Denotes the current beacon value for this session, which is updated via
// CleanExitBeacon::WriteBeaconValue(). When `false`, Chrome is watching for
// browser crashes. When `true`, Chrome has stopped watching for crashes. When
// unset, Chrome has neither started nor stopped watching for crashes.
absl::optional<bool> has_exited_cleanly_ = absl::nullopt;
// Where the clean exit beacon and the variations crash streak may be stored
// for some clients in the Extended Variations Safe Mode experiment.
base::FilePath beacon_file_path_;
};
} // namespace metrics
#endif // COMPONENTS_METRICS_CLEAN_EXIT_BEACON_H_