1 // Copyright 2014 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef COMPONENTS_METRICS_CLEAN_EXIT_BEACON_H_ 6 #define COMPONENTS_METRICS_CLEAN_EXIT_BEACON_H_ 7 8 #include <optional> 9 #include <string> 10 11 #include "base/files/file_path.h" 12 #include "base/memory/raw_ptr.h" 13 #include "base/time/time.h" 14 #include "base/values.h" 15 #include "build/build_config.h" 16 17 class PrefRegistrySimple; 18 class PrefService; 19 20 namespace metrics { 21 22 // The name of the beacon file, which is relative to the user data directory 23 // and used to store the CleanExitBeacon value and the variations crash streak. 24 extern const base::FilePath::CharType kCleanExitBeaconFilename[]; 25 26 // Captures all possible beacon value permutations for two distinct beacons. 27 // Exposed for testing. 28 // 29 // These values are persisted to logs. Entries should not be renumbered and 30 // numeric values should never be reused. 31 enum class CleanExitBeaconConsistency { 32 kCleanClean = 0, 33 kCleanDirty = 1, 34 kCleanMissing = 2, 35 kDirtyClean = 3, 36 kDirtyDirty = 4, 37 kDirtyMissing = 5, 38 kMissingClean = 6, 39 kMissingDirty = 7, 40 kMissingMissing = 8, 41 kMaxValue = kMissingMissing, 42 }; 43 44 // Denotes the state of the beacon file. Exposed for testing. 45 // 46 // These values are persisted to logs. Entries should not be renumbered and 47 // numeric values should never be reused. 48 enum class BeaconFileState { 49 kReadable = 0, 50 kNotDeserializable = 1, 51 kMissingDictionary = 2, 52 kMissingCrashStreak = 3, 53 kMissingBeacon = 4, 54 kMaxValue = kMissingBeacon, 55 }; 56 57 // Reads and updates a beacon used to detect whether the previous browser 58 // process exited cleanly. 59 class CleanExitBeacon { 60 public: 61 // Instantiates a CleanExitBeacon whose value is stored in 62 // |has_exited_cleanly_|. The value is persisted in the beacon file on 63 // platforms that support this mechanism and in Local State on platforms that 64 // don't. 65 // 66 // On Windows, |backup_registry_key| stores a backup of the beacon to verify 67 // that the pref's value corresponds to the registry's. |backup_registry_key| 68 // is ignored on other platforms, but iOS has a similar verification 69 // mechanism embedded inside CleanExitBeacon. 70 // 71 // |user_data_dir| is the path to the client's user data directory. If empty, 72 // the beacon file is not used. 73 CleanExitBeacon(const std::wstring& backup_registry_key, 74 const base::FilePath& user_data_dir, 75 PrefService* local_state); 76 77 virtual ~CleanExitBeacon() = default; 78 79 // Not copyable or movable. 80 CleanExitBeacon(const CleanExitBeacon&) = delete; 81 CleanExitBeacon& operator=(const CleanExitBeacon&) = delete; 82 83 // Initializes the CleanExitBeacon. This includes the following tasks: 84 // 1. Determining if the last session exited cleanly, 85 // 2. Incrementing the crash streak, if necessary, and 86 // 3. Emitting some metrics. 87 void Initialize(); 88 89 // Returns the original value of the beacon. exited_cleanly()90 bool exited_cleanly() const { return did_previous_session_exit_cleanly_; } 91 92 // Returns the original value of the last live timestamp. browser_last_live_timestamp()93 base::Time browser_last_live_timestamp() const { 94 return initial_browser_last_live_timestamp_; 95 } 96 97 // Returns true if Extended Variations Safe Mode is supported on this 98 // platform. Android WebLayer and WebView do not support this. 99 bool IsExtendedSafeModeSupported() const; 100 101 // Sets the beacon value to |exited_cleanly| and writes the value to disk if 102 // the current value (see has_exited_cleanly_) is not already 103 // |exited_cleanly|. Note that on platforms that do not support the beacon 104 // file, the write is scheduled, so the value may not be persisted if the 105 // browser process crashes. 106 // 107 // Also, updates the last live timestamp. 108 // 109 // |is_extended_safe_mode| denotes whether Chrome is about to start watching 110 // for browser crashes early on in startup as a part of Extended Variations 111 // Safe Mode, which is supported by most, but not all, platforms. 112 // 113 // TODO(crbug.com/40850854): Consider removing |is_extended_safe_mode|. 114 void WriteBeaconValue(bool exited_cleanly, 115 bool is_extended_safe_mode = false); 116 117 // Updates the last live timestamp. 118 void UpdateLastLiveTimestamp(); 119 120 const base::FilePath GetUserDataDirForTesting() const; 121 base::FilePath GetBeaconFilePathForTesting() const; 122 123 // Registers local state prefs used by this class. 124 static void RegisterPrefs(PrefRegistrySimple* registry); 125 126 // Updates both Local State and NSUserDefaults beacon values. 127 static void SetStabilityExitedCleanlyForTesting(PrefService* local_state, 128 bool exited_cleanly); 129 130 // Creates and returns a well-formed beacon file contents with the given 131 // values. 132 static std::string CreateBeaconFileContentsForTesting(bool exited_cleanly, 133 int crash_streak); 134 135 // Resets both Local State and NSUserDefaults beacon values. 136 static void ResetStabilityExitedCleanlyForTesting(PrefService* local_state); 137 138 // CHECKs that Chrome exited cleanly. 139 static void EnsureCleanShutdown(PrefService* local_state); 140 141 #if BUILDFLAG(IS_IOS) 142 // Sets the NSUserDefaults beacon value. 143 static void SetUserDefaultsBeacon(bool exited_cleanly); 144 145 // Checks user default value of kUseUserDefaultsForExitedCleanlyBeacon. 146 // Because variations are not initialized early in startup, pair a user 147 // defaults value with the variations config. 148 static bool ShouldUseUserDefaultsBeacon(); 149 150 // Syncs feature kUseUserDefaultsForExitedCleanlyBeacon to NSUserDefaults 151 // kUserDefaultsFeatureFlagForExitedCleanlyBeacon. 152 static void SyncUseUserDefaultsBeacon(); 153 #endif // BUILDFLAG(IS_IOS) 154 155 // Prevents a test browser from performing two clean shutdown steps. First, it 156 // prevents the beacon value from being updated after this function is called. 157 // This prevents the the test browser from signaling that Chrome is shutting 158 // down cleanly. Second, it makes EnsureCleanShutdown() a no-op. 159 static void SkipCleanShutdownStepsForTesting(); 160 161 private: 162 // Returns true if the previous session exited cleanly. Either Local State 163 // or |beacon_file_contents| is used to get this information. Which is used 164 // depends on the client's platform and the existence of a valid beacon file. 165 // Also, records several metrics. 166 // 167 // Should be called only once: at startup. 168 bool DidPreviousSessionExitCleanly(base::Value* beacon_file_contents); 169 170 // Returns true if the beacon file is supported on this platform. Android 171 // WebLayer and WebView do not support this. 172 bool IsBeaconFileSupported() const; 173 174 // Writes |exited_cleanly| and the crash streak to the file located at 175 // |beacon_file_path_|. 176 void WriteBeaconFile(bool exited_cleanly) const; 177 178 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS) 179 // Returns whether Chrome exited cleanly in the previous session according to 180 // the platform-specific beacon (the registry for Windows or NSUserDefaults 181 // for iOS). Returns std::nullopt if the platform-specific location does not 182 // have beacon info. 183 std::optional<bool> ExitedCleanly(); 184 #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_IOS) 185 186 #if BUILDFLAG(IS_IOS) 187 // Returns true if the NSUserDefaults beacon value is set. 188 static bool HasUserDefaultsBeacon(); 189 190 // Returns the NSUserDefaults beacon value. 191 static bool GetUserDefaultsBeacon(); 192 193 // Clears the NSUserDefaults beacon value. 194 static void ResetUserDefaultsBeacon(); 195 #endif // BUILDFLAG(IS_IOS) 196 197 // Indicates whether the CleanExitBeacon has been initialized. 198 bool initialized_ = false; 199 200 // Stores a backup of the beacon. Windows only. 201 const std::wstring backup_registry_key_; 202 203 // Path to the client's user data directory. May be empty. 204 const base::FilePath user_data_dir_; 205 206 const raw_ptr<PrefService> local_state_; 207 208 // This is the value of the last live timestamp from local state at the time 209 // of construction. It is a timestamp from the previous browser session when 210 // the browser was known to be alive. 211 const base::Time initial_browser_last_live_timestamp_; 212 213 bool did_previous_session_exit_cleanly_ = false; 214 215 // Denotes the current beacon value for this session, which is updated via 216 // CleanExitBeacon::WriteBeaconValue(). When `false`, Chrome is watching for 217 // browser crashes. When `true`, Chrome has stopped watching for crashes. When 218 // unset, Chrome has neither started nor stopped watching for crashes. 219 std::optional<bool> has_exited_cleanly_ = std::nullopt; 220 221 // Where the clean exit beacon and the variations crash streak are stored on 222 // platforms that support the beacon file. 223 base::FilePath beacon_file_path_; 224 }; 225 226 } // namespace metrics 227 228 #endif // COMPONENTS_METRICS_CLEAN_EXIT_BEACON_H_ 229