1 // Copyright 2024 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 BASE_ANDROID_PRE_FREEZE_BACKGROUND_MEMORY_TRIMMER_H_ 6 #define BASE_ANDROID_PRE_FREEZE_BACKGROUND_MEMORY_TRIMMER_H_ 7 8 #include <deque> 9 10 #include "base/compiler_specific.h" 11 #include "base/debug/proc_maps_linux.h" 12 #include "base/feature_list.h" 13 #include "base/functional/callback.h" 14 #include "base/memory/post_delayed_memory_reduction_task.h" 15 #include "base/no_destructor.h" 16 #include "base/task/delayed_task_handle.h" 17 #include "base/task/sequenced_task_runner.h" 18 #include "base/timer/timer.h" 19 20 namespace base::android { 21 class MemoryPurgeManagerAndroid; 22 23 BASE_EXPORT BASE_DECLARE_FEATURE(kOnPreFreezeMemoryTrim); 24 BASE_EXPORT BASE_DECLARE_FEATURE(kShouldFreezeSelf); 25 26 // Starting from Android U, apps are frozen shortly after being backgrounded 27 // (with some exceptions). This causes some background tasks for reclaiming 28 // resources in Chrome to not be run until Chrome is foregrounded again (which 29 // completely defeats their purpose). 30 // 31 // To try to avoid this problem, we use the |PostDelayedBackgroundTask| found 32 // below. Prior to Android U, this will simply post the task in the background 33 // with the given delay. From Android U onwards, this will post the task in the 34 // background with the given delay, but will run it sooner if we are about to 35 // be frozen. 36 class BASE_EXPORT PreFreezeBackgroundMemoryTrimmer { 37 public: 38 static PreFreezeBackgroundMemoryTrimmer& Instance(); 39 ~PreFreezeBackgroundMemoryTrimmer() = delete; 40 41 // Posts a delayed task. On versions of Android starting from U, may run the 42 // task sooner if we are backgrounded. In this case, we run the task on the 43 // correct task runner, ignoring the given delay. PostDelayedBackgroundTask(scoped_refptr<base::SequencedTaskRunner> task_runner,const base::Location & from_here,OnceCallback<void (void)> task,base::TimeDelta delay)44 static void PostDelayedBackgroundTask( 45 scoped_refptr<base::SequencedTaskRunner> task_runner, 46 const base::Location& from_here, 47 OnceCallback<void(void)> task, 48 base::TimeDelta delay) LOCKS_EXCLUDED(lock_) { 49 PostDelayedBackgroundTask( 50 task_runner, from_here, 51 BindOnce( 52 [](OnceClosure task, 53 MemoryReductionTaskContext called_from_prefreeze) { 54 std::move(task).Run(); 55 }, 56 std::move(task)), 57 delay); 58 } 59 static void PostDelayedBackgroundTask( 60 scoped_refptr<base::SequencedTaskRunner> task_runner, 61 const base::Location& from_here, 62 OnceCallback<void(MemoryReductionTaskContext)> task, 63 base::TimeDelta delay) LOCKS_EXCLUDED(lock_); 64 65 class PreFreezeMetric { 66 public: 67 virtual ~PreFreezeMetric(); 68 69 // |Measure| should return an amount of memory in bytes, or nullopt if 70 // unable to record the metric for any reason. It is called underneath a 71 // lock, so it should be fast enough to avoid delays (the same lock is held 72 // when unregistering metrics). 73 virtual std::optional<uint64_t> Measure() const = 0; 74 name()75 const std::string& name() const LIFETIME_BOUND { return name_; } 76 77 protected: 78 friend class PreFreezeBackgroundMemoryTrimmer; 79 explicit PreFreezeMetric(const std::string& name); 80 81 private: 82 const std::string name_; 83 }; 84 85 // Registers a new metric to record before and after PreFreeze. Callers are 86 // responsible for making sure that the same metric is not registered 87 // multiple times. 88 // 89 // See |PreFreezeMetric| for details on the metric itself. 90 // 91 // Each time |OnPreFreeze| is run, |metric->Measure()| will be called twice: 92 // - Once directly before any tasks are run; and 93 // - Once two seconds after the first time it was called. 94 // 95 // As an example, calling RegisterMemoryMetric(PrivateMemoryFootprintMetric) 96 // in the Browser process would cause the following metrics to be recorded 2 97 // seconds after the next time |OnPreFreeze| is run: 98 // - "Memory.PreFreeze2.Browser.PrivateMemoryFootprint.Before" 99 // - "Memory.PreFreeze2.Browser.PrivateMemoryFootprint.After" 100 // - "Memory.PreFreeze2.Browser.PrivateMemoryFootprint.Diff" 101 // 102 // See "Memory.PreFreeze2.{process_type}.{name}.{suffix}" for details on the 103 // exact metrics. 104 static void RegisterMemoryMetric(const PreFreezeMetric* metric) 105 LOCKS_EXCLUDED(Instance().lock_); 106 107 static void UnregisterMemoryMetric(const PreFreezeMetric* metric) 108 LOCKS_EXCLUDED(Instance().lock_); 109 110 static bool SelfCompactionIsSupported(); 111 112 // Compacts the memory for the process, and returns the number of bytes 113 // processed on success. 114 static std::optional<int64_t> CompactSelf(); 115 116 static void SetSupportsModernTrimForTesting(bool is_supported); 117 static void ClearMetricsForTesting() LOCKS_EXCLUDED(lock_); 118 size_t GetNumberOfPendingBackgroundTasksForTesting() const 119 LOCKS_EXCLUDED(lock_); 120 size_t GetNumberOfKnownMetricsForTesting() const LOCKS_EXCLUDED(lock_); 121 size_t GetNumberOfValuesBeforeForTesting() const LOCKS_EXCLUDED(lock_); 122 bool DidRegisterTasksForTesting() const; 123 OnPreFreezeForTesting()124 static void OnPreFreezeForTesting() LOCKS_EXCLUDED(lock_) { OnPreFreeze(); } 125 126 static std::optional<int64_t> CompactRegion(debug::MappedMemoryRegion region); 127 128 // Called when Chrome is about to be frozen. Runs as many delayed tasks as 129 // possible immediately, before we are frozen. 130 static void OnPreFreeze() LOCKS_EXCLUDED(lock_); 131 132 static void OnSelfFreeze() LOCKS_EXCLUDED(lock_); 133 134 static bool SupportsModernTrim(); 135 static bool ShouldUseModernTrim(); 136 static bool IsTrimMemoryBackgroundCritical(); 137 138 private: 139 friend class base::NoDestructor<PreFreezeBackgroundMemoryTrimmer>; 140 friend jboolean JNI_MemoryPurgeManager_IsOnPreFreezeMemoryTrimEnabled( 141 JNIEnv* env); 142 friend class base::android::MemoryPurgeManagerAndroid; 143 friend class base::OneShotDelayedBackgroundTimer; 144 friend class PreFreezeBackgroundMemoryTrimmerTest; 145 friend class PreFreezeSelfCompactionTest; 146 147 // We use our own implementation here, based on |PostCancelableDelayedTask|, 148 // rather than relying on something like |base::OneShotTimer|, since 149 // |base::OneShotTimer| doesn't support things like immediately running our 150 // task from a different sequence, and some |base::OneShotTimer| 151 // functionality (e.g. |FireNow|) only works with the default task runner. 152 class BackgroundTask final { 153 public: 154 static std::unique_ptr<BackgroundTask> Create( 155 scoped_refptr<base::SequencedTaskRunner> task_runner, 156 const base::Location& from_here, 157 OnceCallback<void(MemoryReductionTaskContext)> task, 158 base::TimeDelta delay); 159 160 explicit BackgroundTask( 161 scoped_refptr<base::SequencedTaskRunner> task_runner); 162 ~BackgroundTask(); 163 164 static void RunNow(std::unique_ptr<BackgroundTask> background_task); 165 166 void Run(MemoryReductionTaskContext from_pre_freeze); 167 168 void CancelTask(); 169 170 private: 171 friend class PreFreezeBackgroundMemoryTrimmer; 172 void Start(const Location& from_here, 173 TimeDelta delay, 174 OnceCallback<void(MemoryReductionTaskContext)> task); 175 void StartInternal(const Location& from_here, 176 TimeDelta delay, 177 OnceClosure task); 178 scoped_refptr<base::SequencedTaskRunner> task_runner_; 179 base::DelayedTaskHandle task_handle_; 180 181 OnceCallback<void(MemoryReductionTaskContext)> task_; 182 }; 183 184 PreFreezeBackgroundMemoryTrimmer(); 185 186 static std::optional<int64_t> CompactMemory( 187 std::vector<debug::MappedMemoryRegion> regions); 188 189 void RegisterMemoryMetricInternal(const PreFreezeMetric* metric) 190 EXCLUSIVE_LOCKS_REQUIRED(lock_); 191 192 void UnregisterMemoryMetricInternal(const PreFreezeMetric* metric) 193 EXCLUSIVE_LOCKS_REQUIRED(lock_); 194 static void UnregisterBackgroundTask(BackgroundTask*) LOCKS_EXCLUDED(lock_); 195 196 void UnregisterBackgroundTaskInternal(BackgroundTask*) LOCKS_EXCLUDED(lock_); 197 198 static void RegisterPrivateMemoryFootprintMetric() LOCKS_EXCLUDED(lock_); 199 void RegisterPrivateMemoryFootprintMetricInternal() LOCKS_EXCLUDED(lock_); 200 201 void PostDelayedBackgroundTaskInternal( 202 scoped_refptr<base::SequencedTaskRunner> task_runner, 203 const base::Location& from_here, 204 OnceCallback<void(MemoryReductionTaskContext)> task, 205 base::TimeDelta delay) LOCKS_EXCLUDED(lock_); 206 void PostDelayedBackgroundTaskModern( 207 scoped_refptr<base::SequencedTaskRunner> task_runner, 208 const base::Location& from_here, 209 OnceCallback<void(MemoryReductionTaskContext)> task, 210 base::TimeDelta delay) LOCKS_EXCLUDED(lock_); 211 BackgroundTask* PostDelayedBackgroundTaskModernHelper( 212 scoped_refptr<base::SequencedTaskRunner> task_runner, 213 const base::Location& from_here, 214 OnceCallback<void(MemoryReductionTaskContext)> task, 215 base::TimeDelta delay) EXCLUSIVE_LOCKS_REQUIRED(lock_); 216 217 void OnPreFreezeInternal() LOCKS_EXCLUDED(lock_); 218 219 void PostMetricsTasksIfModern() EXCLUSIVE_LOCKS_REQUIRED(lock_); 220 void PostMetricsTask() EXCLUSIVE_LOCKS_REQUIRED(lock_); 221 void RecordMetrics() LOCKS_EXCLUDED(lock_); 222 223 mutable base::Lock lock_; 224 std::deque<std::unique_ptr<BackgroundTask>> background_tasks_ 225 GUARDED_BY(lock_); 226 std::vector<const PreFreezeMetric*> metrics_ GUARDED_BY(lock_); 227 // When a metrics task is posted (see |RecordMetrics|), the values of each 228 // metric before any tasks are run are saved here. The "i"th entry corresponds 229 // to the "i"th entry in |metrics_|. When there is no pending metrics task, 230 // |values_before_| should be empty. 231 std::vector<std::optional<uint64_t>> values_before_ GUARDED_BY(lock_); 232 bool supports_modern_trim_; 233 }; 234 235 } // namespace base::android 236 237 #endif // BASE_ANDROID_PRE_FREEZE_BACKGROUND_MEMORY_TRIMMER_H_ 238