| // Copyright 2014 The Chromium Authors |
| // 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 <optional> |
| #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" |
| |
| class PrefRegistrySimple; |
| class PrefService; |
| |
| namespace metrics { |
| |
| // The name of the beacon file, which is relative to the user data directory |
| // and used to store the CleanExitBeacon value and the variations crash streak. |
| extern const base::FilePath::CharType kCleanExitBeaconFilename[]; |
| |
| // 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 |
| // |has_exited_cleanly_|. The value is persisted in the beacon file on |
| // platforms that support this mechanism and in Local State on platforms that |
| // don't. |
| // |
| // 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, |
| // the beacon file is not used. |
| CleanExitBeacon(const std::wstring& backup_registry_key, |
| const base::FilePath& user_data_dir, |
| PrefService* local_state); |
| |
| 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_; |
| } |
| |
| // Returns true if Extended Variations Safe Mode is supported on this |
| // platform. Android WebLayer and WebView do not support this. |
| bool IsExtendedSafeModeSupported() const; |
| |
| // Sets the beacon value to |exited_cleanly| and writes the value to disk if |
| // the current value (see has_exited_cleanly_) is not already |
| // |exited_cleanly|. Note that on platforms that do not support the beacon |
| // file, the write is scheduled, so the value may not be persisted if the |
| // browser process crashes. |
| // |
| // Also, updates the last live timestamp. |
| // |
| // |is_extended_safe_mode| denotes whether Chrome is about to start watching |
| // for browser crashes early on in startup as a part of Extended Variations |
| // Safe Mode, which is supported by most, but not all, platforms. |
| // |
| // TODO(crbug.com/40850854): Consider removing |is_extended_safe_mode|. |
| 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); |
| |
| // Creates and returns a well-formed beacon file contents with the given |
| // values. |
| static std::string CreateBeaconFileContentsForTesting(bool exited_cleanly, |
| int crash_streak); |
| |
| // 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 platform and the existence of a valid beacon file. |
| // Also, records several metrics. |
| // |
| // Should be called only once: at startup. |
| bool DidPreviousSessionExitCleanly(base::Value* beacon_file_contents); |
| |
| // Returns true if the beacon file is supported on this platform. Android |
| // WebLayer and WebView do not support this. |
| bool IsBeaconFileSupported() const; |
| |
| // 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 std::nullopt if the platform-specific location does not |
| // have beacon info. |
| std::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) |
| |
| // 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_; |
| |
| 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. |
| std::optional<bool> has_exited_cleanly_ = std::nullopt; |
| |
| // Where the clean exit beacon and the variations crash streak are stored on |
| // platforms that support the beacon file. |
| base::FilePath beacon_file_path_; |
| }; |
| |
| } // namespace metrics |
| |
| #endif // COMPONENTS_METRICS_CLEAN_EXIT_BEACON_H_ |