// 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 #include #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 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 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 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_