blob: c0bd53eb7b890392f55068993047058b92575b7f [file] [log] [blame]
Bartek Nowierski8daf56e2021-01-27 11:53:191// Copyright (c) 2021 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Benoit Lize7ee77d32021-03-04 17:26:145#include "content/common/partition_alloc_support.h"
Bartek Nowierski8daf56e2021-01-27 11:53:196
7#include <string>
8
9#include "base/allocator/allocator_shim.h"
10#include "base/allocator/buildflags.h"
Bartek Nowierskic9f1e7ef2021-06-28 14:09:3011#include "base/allocator/partition_allocator/partition_alloc_config.h"
Bartek Nowierski8daf56e2021-01-27 11:53:1912#include "base/allocator/partition_allocator/partition_alloc_features.h"
Anton Bikineevfd91bbc2021-03-21 12:35:4913#include "base/allocator/partition_allocator/starscan/pcscan.h"
Michael Lippautzd34f7b2c2021-04-21 08:32:2614#include "base/allocator/partition_allocator/starscan/pcscan_scheduling.h"
Anton Bikineev8cf9e1e32021-04-26 23:52:3815#include "base/allocator/partition_allocator/starscan/stack/stack.h"
Benoit Lize6ae00272021-02-25 15:56:5316#include "base/allocator/partition_allocator/thread_cache.h"
Michael Lippautzd34f7b2c2021-04-21 08:32:2617#include "base/bind.h"
18#include "base/callback.h"
Bartek Nowierski8daf56e2021-01-27 11:53:1919#include "base/feature_list.h"
Michael Lippautzd34f7b2c2021-04-21 08:32:2620#include "base/no_destructor.h"
Bartek Nowierski8daf56e2021-01-27 11:53:1921#include "build/build_config.h"
22#include "content/public/common/content_switches.h"
23
24#if defined(OS_ANDROID)
25#include "base/system/sys_info.h"
26#endif
27
Benoit Lize6d3d74ef2021-09-21 13:07:5528#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
29#include "base/allocator/partition_allocator/memory_reclaimer.h"
30#include "base/threading/thread_task_runner_handle.h"
31#endif
32
Bartek Nowierski8daf56e2021-01-27 11:53:1933namespace content {
34namespace internal {
35
36namespace {
37
Anton Bikineeva39a995b2021-03-04 13:03:0038void SetProcessNameForPCScan(const std::string& process_type) {
39 const char* name = [&process_type] {
40 if (process_type.empty()) {
41 // Empty means browser process.
42 return "Browser";
43 }
44 if (process_type == switches::kRendererProcess)
45 return "Renderer";
46 if (process_type == switches::kGpuProcess)
47 return "Gpu";
48 if (process_type == switches::kUtilityProcess)
49 return "Utility";
50 return static_cast<const char*>(nullptr);
51 }();
52
53 if (name) {
Anton Bikineev37a0e6c2021-04-28 08:51:4254 base::internal::PCScan::SetProcessName(name);
Anton Bikineeva39a995b2021-03-04 13:03:0055 }
56}
57
Anton Bikineevea8859b2021-04-29 19:17:2258bool EnablePCScanForMallocPartitionsIfNeeded() {
Bartek Nowierskibba18462021-06-02 02:34:4759#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN)
Anton Bikineev27325272021-09-23 13:43:1160 using Config = base::internal::PCScan::InitConfig;
Bartek Nowierski8daf56e2021-01-27 11:53:1961 DCHECK(base::FeatureList::GetInstance());
62 if (base::FeatureList::IsEnabled(base::features::kPartitionAllocPCScan)) {
Anton Bikineev27325272021-09-23 13:43:1163 base::allocator::EnablePCScan({Config::WantedWriteProtectionMode::kEnabled,
64 Config::SafepointMode::kEnabled});
Anton Bikineevea8859b2021-04-29 19:17:2265 return true;
Bartek Nowierski8daf56e2021-01-27 11:53:1966 }
Bartek Nowierskibba18462021-06-02 02:34:4767#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN)
Anton Bikineevea8859b2021-04-29 19:17:2268 return false;
Bartek Nowierski8daf56e2021-01-27 11:53:1969}
70
Anton Bikineevea8859b2021-04-29 19:17:2271bool EnablePCScanForMallocPartitionsInBrowserProcessIfNeeded() {
Bartek Nowierskibba18462021-06-02 02:34:4772#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN)
Anton Bikineev27325272021-09-23 13:43:1173 using Config = base::internal::PCScan::InitConfig;
Bartek Nowierski8daf56e2021-01-27 11:53:1974 DCHECK(base::FeatureList::GetInstance());
75 if (base::FeatureList::IsEnabled(
76 base::features::kPartitionAllocPCScanBrowserOnly)) {
Anton Bikineev27325272021-09-23 13:43:1177 const Config::WantedWriteProtectionMode wp_mode =
78 base::FeatureList::IsEnabled(base::features::kPartitionAllocDCScan)
79 ? Config::WantedWriteProtectionMode::kEnabled
80 : Config::WantedWriteProtectionMode::kDisabled;
Anton Bikineev6b5ab952021-05-19 23:57:5781#if !defined(PA_STARSCAN_UFFD_WRITE_PROTECTOR_SUPPORTED)
Anton Bikineev27325272021-09-23 13:43:1182 CHECK_EQ(Config::WantedWriteProtectionMode::kDisabled, wp_mode)
Anton Bikineev6b5ab952021-05-19 23:57:5783 << "DCScan is currently only supported on Linux based systems";
84#endif
Anton Bikineev27325272021-09-23 13:43:1185 base::allocator::EnablePCScan({wp_mode, Config::SafepointMode::kEnabled});
Anton Bikineevea8859b2021-04-29 19:17:2286 return true;
Bartek Nowierski8daf56e2021-01-27 11:53:1987 }
Bartek Nowierskibba18462021-06-02 02:34:4788#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN)
Anton Bikineevea8859b2021-04-29 19:17:2289 return false;
Bartek Nowierski8daf56e2021-01-27 11:53:1990}
91
Anton Bikineevd2509332021-09-16 17:14:5792bool EnablePCScanForMallocPartitionsInRendererProcessIfNeeded() {
93#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN)
Anton Bikineev27325272021-09-23 13:43:1194 using Config = base::internal::PCScan::InitConfig;
Anton Bikineevd2509332021-09-16 17:14:5795 DCHECK(base::FeatureList::GetInstance());
96 if (base::FeatureList::IsEnabled(
97 base::features::kPartitionAllocPCScanRendererOnly)) {
Anton Bikineev27325272021-09-23 13:43:1198 const Config::WantedWriteProtectionMode wp_mode =
99 base::FeatureList::IsEnabled(base::features::kPartitionAllocDCScan)
100 ? Config::WantedWriteProtectionMode::kEnabled
101 : Config::WantedWriteProtectionMode::kDisabled;
Anton Bikineevd2509332021-09-16 17:14:57102#if !defined(PA_STARSCAN_UFFD_WRITE_PROTECTOR_SUPPORTED)
Anton Bikineev27325272021-09-23 13:43:11103 CHECK_EQ(Config::WantedWriteProtectionMode::kDisabled, wp_mode)
Anton Bikineevd2509332021-09-16 17:14:57104 << "DCScan is currently only supported on Linux based systems";
105#endif
Anton Bikineev27325272021-09-23 13:43:11106 base::allocator::EnablePCScan({wp_mode, Config::SafepointMode::kDisabled});
Anton Bikineevd2509332021-09-16 17:14:57107 return true;
108 }
109#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN)
110 return false;
111}
112
Keishi Hattoriaf499ac2021-09-22 23:45:04113} // namespace
Bartek Nowierski8daf56e2021-01-27 11:53:19114
115void ReconfigurePartitionForKnownProcess(const std::string& process_type) {
116 DCHECK_NE(process_type, switches::kZygoteProcess);
Keishi Hattoriaf499ac2021-09-22 23:45:04117 // TODO(keishi): Move the code to enable BRP back here after Finch
118 // experiments.
Bartek Nowierski8daf56e2021-01-27 11:53:19119}
120
Benoit Lize6ae00272021-02-25 15:56:53121PartitionAllocSupport::PartitionAllocSupport() = default;
122
Bartek Nowierski8c30bb92021-01-27 21:43:24123void PartitionAllocSupport::ReconfigureEarlyish(
124 const std::string& process_type) {
125 {
126 base::AutoLock scoped_lock(lock_);
127 // TODO(bartekn): Switch to DCHECK once confirmed there are no issues.
128 CHECK(!called_earlyish_)
129 << "ReconfigureEarlyish was already called for process '"
130 << established_process_type_ << "'; current process: '" << process_type
131 << "'";
132
133 called_earlyish_ = true;
134 established_process_type_ = process_type;
135 }
136
Bartek Nowierski8daf56e2021-01-27 11:53:19137 if (process_type != switches::kZygoteProcess) {
138 ReconfigurePartitionForKnownProcess(process_type);
139 }
140
141 // These initializations are only relevant for PartitionAlloc-Everywhere
142 // builds.
143#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
Bartek Nowierski8daf56e2021-01-27 11:53:19144 base::allocator::EnablePartitionAllocMemoryReclaimer();
Bartek Nowierski8daf56e2021-01-27 11:53:19145#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
146}
147
Bartek Nowierski8c30bb92021-01-27 21:43:24148void PartitionAllocSupport::ReconfigureAfterZygoteFork(
149 const std::string& process_type) {
150 {
151 base::AutoLock scoped_lock(lock_);
152 // TODO(bartekn): Switch to DCHECK once confirmed there are no issues.
153 CHECK(!called_after_zygote_fork_)
154 << "ReconfigureAfterZygoteFork was already called for process '"
155 << established_process_type_ << "'; current process: '" << process_type
156 << "'";
157 DCHECK(called_earlyish_)
158 << "Attempt to call ReconfigureAfterZygoteFork without calling "
159 "ReconfigureEarlyish; current process: '"
160 << process_type << "'";
161 DCHECK_EQ(established_process_type_, switches::kZygoteProcess)
162 << "Attempt to call ReconfigureAfterZygoteFork while "
163 "ReconfigureEarlyish was called on non-zygote process '"
164 << established_process_type_ << "'; current process: '" << process_type
165 << "'";
166
167 called_after_zygote_fork_ = true;
168 established_process_type_ = process_type;
169 }
170
Bartek Nowierskiaacb40a2021-01-29 18:15:26171 if (process_type != switches::kZygoteProcess) {
172 ReconfigurePartitionForKnownProcess(process_type);
173 }
Bartek Nowierski8daf56e2021-01-27 11:53:19174}
175
Bartek Nowierski8c30bb92021-01-27 21:43:24176void PartitionAllocSupport::ReconfigureAfterFeatureListInit(
Bartek Nowierski8daf56e2021-01-27 11:53:19177 const std::string& process_type) {
Bartek Nowierski8c30bb92021-01-27 21:43:24178 {
179 base::AutoLock scoped_lock(lock_);
180 // Avoid initializing more than once.
181 // TODO(bartekn): See if can be converted to (D)CHECK.
182 if (called_after_feature_list_init_) {
183 DCHECK_EQ(established_process_type_, process_type)
184 << "ReconfigureAfterFeatureListInit was already called for process '"
185 << established_process_type_ << "'; current process: '"
186 << process_type << "'";
187 return;
188 }
189 DCHECK(called_earlyish_)
190 << "Attempt to call ReconfigureAfterFeatureListInit without calling "
191 "ReconfigureEarlyish; current process: '"
192 << process_type << "'";
193 DCHECK_NE(established_process_type_, switches::kZygoteProcess)
194 << "Attempt to call ReconfigureAfterFeatureListInit without calling "
195 "ReconfigureAfterZygoteFork; current process: '"
196 << process_type << "'";
197 DCHECK_EQ(established_process_type_, process_type)
198 << "ReconfigureAfterFeatureListInit wasn't called for an already "
199 "established process '"
200 << established_process_type_ << "'; current process: '" << process_type
201 << "'";
202
203 called_after_feature_list_init_ = true;
204 }
205
Bartek Nowierski8daf56e2021-01-27 11:53:19206 DCHECK_NE(process_type, switches::kZygoteProcess);
207 // TODO(bartekn): Switch to DCHECK once confirmed there are no issues.
208 CHECK(base::FeatureList::GetInstance());
209
Keishi Hattoriaf499ac2021-09-22 23:45:04210 bool enable_brp = false;
Keishi Hattorib6c842922021-09-22 23:48:58211#if BUILDFLAG(USE_BACKUP_REF_PTR)
Keishi Hattoriaf499ac2021-09-22 23:45:04212 if (base::FeatureList::IsEnabled(
213 base::features::kPartitionAllocBackupRefPtr)) {
214 // No specified process type means this is the Browser process.
215 enable_brp = process_type.empty();
216 if (base::features::kBackupRefPtrEnabledProcessesParam.Get() ==
217 base::features::BackupRefPtrEnabledProcesses::kBrowserAndRenderer) {
218 enable_brp |= process_type == switches::kRendererProcess;
219 }
220 base::allocator::ConfigurePartitionBackupRefPtrSupport(enable_brp);
221 }
222#endif
223
Bartek Nowierskicd920ae12021-02-25 12:17:30224#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
225 base::allocator::ReconfigurePartitionAllocLazyCommit();
Benoit Lize2dd8eec2021-03-05 08:58:33226#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
Bartek Nowierskicd920ae12021-02-25 12:17:30227
Keishi Hattorib6c842922021-09-22 23:48:58228 // Don't enable PCScan if BRP is enabled.
229 if (enable_brp)
230 return;
231
Anton Bikineevea8859b2021-04-29 19:17:22232 bool scan_enabled = EnablePCScanForMallocPartitionsIfNeeded();
Bartek Nowierski8daf56e2021-01-27 11:53:19233 // No specified process type means this is the Browser process.
234 if (process_type.empty()) {
Anton Bikineevea8859b2021-04-29 19:17:22235 scan_enabled = scan_enabled ||
236 EnablePCScanForMallocPartitionsInBrowserProcessIfNeeded();
Bartek Nowierski8daf56e2021-01-27 11:53:19237 }
Anton Bikineevd2509332021-09-16 17:14:57238 if (process_type == switches::kRendererProcess) {
239 scan_enabled = scan_enabled ||
240 EnablePCScanForMallocPartitionsInRendererProcessIfNeeded();
241 }
Anton Bikineevea8859b2021-04-29 19:17:22242 if (scan_enabled) {
243 if (base::FeatureList::IsEnabled(
244 base::features::kPartitionAllocPCScanStackScanning)) {
Nohemi Fernandez1386e84c2021-04-27 14:57:15245#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
Anton Bikineevea8859b2021-04-29 19:17:22246 base::internal::PCScan::EnableStackScanning();
247 // Notify PCScan about the main thread.
248 base::internal::PCScan::NotifyThreadCreated(
249 base::internal::GetStackTop());
250#endif
251 }
Hannes Payera33df7f2021-06-22 18:44:14252 if (base::FeatureList::IsEnabled(
253 base::features::kPartitionAllocPCScanImmediateFreeing)) {
254 base::internal::PCScan::EnableImmediateFreeing();
255 }
Anton Bikineev7f11c212021-08-24 12:03:09256 if (base::FeatureList::IsEnabled(
257 base::features::kPartitionAllocPCScanEagerClearing)) {
258 base::internal::PCScan::SetClearType(
259 base::internal::PCScan::ClearType::kEager);
260 }
Anton Bikineevea8859b2021-04-29 19:17:22261 SetProcessNameForPCScan(process_type);
262 }
Bartek Nowierski8daf56e2021-01-27 11:53:19263}
264
Benoit Lize7ee77d32021-03-04 17:26:14265void PartitionAllocSupport::ReconfigureAfterTaskRunnerInit(
Benoit Lize6ae00272021-02-25 15:56:53266 const std::string& process_type) {
267 {
268 base::AutoLock scoped_lock(lock_);
269
270 // Init only once.
271 if (called_after_thread_pool_init_)
272 return;
273
274 DCHECK_EQ(established_process_type_, process_type);
275 // Enforce ordering.
276 DCHECK(called_earlyish_);
277 DCHECK(called_after_feature_list_init_);
278
279 called_after_thread_pool_init_ = true;
280 }
281
Benoit Lize6ae00272021-02-25 15:56:53282#if defined(PA_THREAD_CACHE_SUPPORTED) && \
283 BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
Benoit Lize7ee77d32021-03-04 17:26:14284 // This should be called in specific processes, as the main thread is
285 // initialized later.
286 DCHECK(process_type != switches::kZygoteProcess);
287
Benoit Lize2066b732021-08-20 15:34:35288 auto& registry = base::internal::ThreadCacheRegistry::Instance();
289 registry.StartPeriodicPurge();
Benoit Lize2dd8eec2021-03-05 08:58:33290
291#if defined(OS_ANDROID)
Benoit Lize2066b732021-08-20 15:34:35292 // Lower thread cache limits to avoid stranding too much memory in the caches.
293 if (base::SysInfo::IsLowEndDevice()) {
294 registry.SetThreadCacheMultiplier(
295 base::internal::ThreadCache::kDefaultMultiplier / 2.);
Benoit Lize6ae00272021-02-25 15:56:53296 }
Benoit Lize2066b732021-08-20 15:34:35297#endif // defined(OS_ANDROID)
Benoit Lize04f5dbb2021-03-25 19:21:30298
299 // Renderer processes are more performance-sensitive, increase thread cache
300 // limits.
Benoit Lize006cfcb2021-03-29 16:18:52301 if (process_type == switches::kRendererProcess &&
302 base::FeatureList::IsEnabled(
303 base::features::kPartitionAllocLargeThreadCacheSize)) {
Benoit Lize8e471f2f2021-03-30 13:29:10304#if defined(OS_ANDROID) && !defined(ARCH_CPU_64_BITS)
305 // Don't use a higher threshold on Android 32 bits, as long as memory usage
306 // is not carefully tuned. Only control the threshold here to avoid changing
307 // the rest of the code below.
308 // As of 2021, 64 bits Android devices are not memory constrained.
Benoit Lize4443f052021-09-13 13:27:02309 largest_cached_size_ =
310 base::internal::ThreadCacheLimits::kDefaultSizeThreshold;
Benoit Lize8e471f2f2021-03-30 13:29:10311#else
Benoit Lize4443f052021-09-13 13:27:02312 largest_cached_size_ =
313 base::internal::ThreadCacheLimits::kLargeSizeThreshold;
Benoit Lize04f5dbb2021-03-25 19:21:30314 base::internal::ThreadCache::SetLargestCachedSize(
Benoit Lize4443f052021-09-13 13:27:02315 base::internal::ThreadCacheLimits::kLargeSizeThreshold);
Benoit Lize8e471f2f2021-03-30 13:29:10316#endif // defined(OS_ANDROID) && !defined(ARCH_CPU_64_BITS)
Benoit Lize04f5dbb2021-03-25 19:21:30317 }
318
Benoit Lize6ae00272021-02-25 15:56:53319#endif // defined(PA_THREAD_CACHE_SUPPORTED) &&
320 // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
Michael Lippautzd34f7b2c2021-04-21 08:32:26321
322 if (base::FeatureList::IsEnabled(
323 base::features::kPartitionAllocPCScanMUAwareScheduler)) {
324 // Assign PCScan a task-based scheduling backend.
325 static base::NoDestructor<base::internal::MUAwareTaskBasedBackend>
326 mu_aware_task_based_backend{
Anton Bikineev37a0e6c2021-04-28 08:51:42327 base::internal::PCScan::scheduler(),
Michael Lippautzd34f7b2c2021-04-21 08:32:26328 base::BindRepeating([](base::TimeDelta delay) {
Anton Bikineev37a0e6c2021-04-28 08:51:42329 base::internal::PCScan::PerformDelayedScan(delay);
Michael Lippautzd34f7b2c2021-04-21 08:32:26330 })};
Anton Bikineev37a0e6c2021-04-28 08:51:42331 base::internal::PCScan::scheduler().SetNewSchedulingBackend(
Michael Lippautzd34f7b2c2021-04-21 08:32:26332 *mu_aware_task_based_backend.get());
333 }
Benoit Lize6d3d74ef2021-09-21 13:07:55334
335#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
336 base::PartitionAllocMemoryReclaimer::Instance()->Start(
337 base::ThreadTaskRunnerHandle::Get());
338#endif
Benoit Lize6ae00272021-02-25 15:56:53339}
340
Benoit Lize481c0c72021-03-30 10:09:50341void PartitionAllocSupport::OnForegrounded() {
342#if defined(PA_THREAD_CACHE_SUPPORTED) && \
343 BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
344 {
345 base::AutoLock scoped_lock(lock_);
346 if (established_process_type_ != switches::kRendererProcess)
347 return;
348 }
349
350 base::internal::ThreadCache::SetLargestCachedSize(largest_cached_size_);
351#endif // defined(PA_THREAD_CACHE_SUPPORTED) &&
352 // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
353}
354
355void PartitionAllocSupport::OnBackgrounded() {
356#if defined(PA_THREAD_CACHE_SUPPORTED) && \
357 BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
358 {
359 base::AutoLock scoped_lock(lock_);
360 if (established_process_type_ != switches::kRendererProcess)
361 return;
362 }
363
364 // Performance matters less for background renderers, don't pay the memory
365 // cost.
366 //
367 // TODO(lizeb): Consider forcing a one-off thread cache purge.
368 base::internal::ThreadCache::SetLargestCachedSize(
Benoit Lize4443f052021-09-13 13:27:02369 base::internal::ThreadCacheLimits::kDefaultSizeThreshold);
Benoit Lize481c0c72021-03-30 10:09:50370#endif // defined(PA_THREAD_CACHE_SUPPORTED) &&
371 // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
372}
373
Bartek Nowierski8daf56e2021-01-27 11:53:19374} // namespace internal
Bartek Nowierskiaacb40a2021-01-29 18:15:26375} // namespace content