From a57023635ec3266eb667c4689fbb27bffe331b1d Mon Sep 17 00:00:00 2001 From: Arthur Sonzogni Date: Mon, 19 Dec 2022 13:18:54 +0000 Subject: [PATCH] [PA] Merge the partition_alloc_support files. There are two partition_alloc_support file defined, one in content/ and one in base/. I would like to use the content/ one to enable BackupRefPtr and DanglingPointerDetector in unittests. The problem: it will be used in base/, which shouldn't depend on content/ This patch merged the two. Bug: 1400059,1400058 Change-Id: I317c5694cb8cde793205ada7401882f79e93c065 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4110828 Reviewed-by: Bartek Nowierski Auto-Submit: Arthur Sonzogni Commit-Queue: Bartek Nowierski Cr-Commit-Position: refs/heads/main@{#1084914} --- base/allocator/partition_alloc_features.cc | 6 + base/allocator/partition_alloc_features.h | 1 + base/allocator/partition_alloc_support.cc | 551 +++++++++++++++++- base/allocator/partition_alloc_support.h | 67 ++- content/app/content_main_runner_impl.cc | 21 +- .../browser/web_contents/web_contents_impl.cc | 1 - content/common/BUILD.gn | 2 - content/common/OWNERS | 2 - content/common/partition_alloc_support.cc | 539 ----------------- content/common/partition_alloc_support.h | 79 --- content/gpu/gpu_main.cc | 4 +- content/public/common/content_features.cc | 3 - content/renderer/render_thread_impl.cc | 6 +- content/renderer/renderer_main.cc | 6 +- content/utility/utility_main.cc | 4 +- 15 files changed, 634 insertions(+), 658 deletions(-) delete mode 100644 content/common/partition_alloc_support.cc delete mode 100644 content/common/partition_alloc_support.h diff --git a/base/allocator/partition_alloc_features.cc b/base/allocator/partition_alloc_features.cc index 4f980fceabdec..ad59fdb5575b1 100644 --- a/base/allocator/partition_alloc_features.cc +++ b/base/allocator/partition_alloc_features.cc @@ -157,6 +157,12 @@ const base::FeatureParam AlternateBucketDistributionMode::kDefault, &kPartitionAllocAlternateDistributionOption}; +// Configures whether we set a lower limit for renderers that do not have a main +// frame, similar to the limit that is already done for backgrounded renderers. +BASE_FEATURE(kLowerPAMemoryLimitForNonMainRenderers, + "LowerPAMemoryLimitForNonMainRenderers", + FEATURE_DISABLED_BY_DEFAULT); + // If enabled, switches PCScan scheduling to a mutator-aware scheduler. Does not // affect whether PCScan is enabled itself. BASE_FEATURE(kPartitionAllocPCScanMUAwareScheduler, diff --git a/base/allocator/partition_alloc_features.h b/base/allocator/partition_alloc_features.h index ac9d77e2fdbba..001944a4f8a5b 100644 --- a/base/allocator/partition_alloc_features.h +++ b/base/allocator/partition_alloc_features.h @@ -115,6 +115,7 @@ extern const BASE_EXPORT base::FeatureParam extern const BASE_EXPORT base::FeatureParam kPartitionAllocAlternateBucketDistributionParam; +BASE_EXPORT BASE_DECLARE_FEATURE(kLowerPAMemoryLimitForNonMainRenderers); BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocPCScanMUAwareScheduler); BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocPCScanStackScanning); BASE_EXPORT BASE_DECLARE_FEATURE(kPartitionAllocDCScan); diff --git a/base/allocator/partition_alloc_support.cc b/base/allocator/partition_alloc_support.cc index 02c4d6ec7ded9..8ce3887efa078 100644 --- a/base/allocator/partition_alloc_support.cc +++ b/base/allocator/partition_alloc_support.cc @@ -11,14 +11,21 @@ #include #include "base/allocator/partition_alloc_features.h" +#include "base/allocator/partition_alloc_support.h" #include "base/allocator/partition_allocator/allocation_guard.h" #include "base/allocator/partition_allocator/dangling_raw_ptr_checks.h" #include "base/allocator/partition_allocator/memory_reclaimer.h" +#include "base/allocator/partition_allocator/page_allocator.h" #include "base/allocator/partition_allocator/partition_alloc_base/debug/alias.h" #include "base/allocator/partition_allocator/partition_alloc_buildflags.h" #include "base/allocator/partition_allocator/partition_alloc_check.h" #include "base/allocator/partition_allocator/partition_alloc_config.h" #include "base/allocator/partition_allocator/partition_lock.h" +#include "base/allocator/partition_allocator/shim/allocator_shim.h" +#include "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h" +#include "base/allocator/partition_allocator/starscan/pcscan.h" +#include "base/allocator/partition_allocator/starscan/pcscan_scheduling.h" +#include "base/allocator/partition_allocator/starscan/stack/stack.h" #include "base/allocator/partition_allocator/thread_cache.h" #include "base/bind.h" #include "base/callback.h" @@ -28,6 +35,9 @@ #include "base/debug/task_trace.h" #include "base/feature_list.h" #include "base/immediate_crash.h" +#include "base/location.h" +#include "base/memory/nonscannable_memory.h" +#include "base/memory/raw_ptr_asan_service.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" @@ -40,6 +50,7 @@ #include "base/time/time.h" #include "base/timer/timer.h" #include "base/trace_event/base_tracing.h" +#include "build/build_config.h" #include "third_party/abseil-cpp/absl/types/optional.h" #if BUILDFLAG(STARSCAN) @@ -48,11 +59,27 @@ #include "base/allocator/partition_allocator/starscan/stats_reporter.h" #endif // BUILDFLAG(STARSCAN) -namespace base { -namespace allocator { +#if BUILDFLAG(IS_ANDROID) +#include "base/system/sys_info.h" +#endif + +#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) +#include "base/allocator/partition_allocator/memory_reclaimer.h" +#endif + +namespace base::allocator { namespace { +// This is defined in content/public/common/content_switches.h, which is not +// accessible in ::base. They must be kept in sync. +namespace switches { +constexpr char kGpuProcess[] = "gpu-process"; +constexpr char kRendererProcess[] = "renderer"; +constexpr char kUtilityProcess[] = "utility"; +constexpr char kZygoteProcess[] = "zygote"; +} // namespace switches + #if defined(PA_ALLOW_PCSCAN) #if BUILDFLAG(ENABLE_BASE_TRACING) @@ -213,8 +240,9 @@ void StartThreadCachePeriodicPurge() { void StartMemoryReclaimer(scoped_refptr task_runner) { // Can be called several times. static bool is_memory_reclaimer_running = false; - if (is_memory_reclaimer_running) + if (is_memory_reclaimer_running) { return; + } is_memory_reclaimer_running = true; // The caller of the API fully controls where running the reclaim. @@ -261,14 +289,17 @@ std::map ProposeSyntheticFinchTrials() { // default behavior), but don't enable BRP protection. [[maybe_unused]] bool brp_truly_enabled = false; #if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - if (FeatureList::IsEnabled(features::kPartitionAllocBackupRefPtr)) + if (FeatureList::IsEnabled(features::kPartitionAllocBackupRefPtr)) { brp_finch_enabled = true; + } if (brp_finch_enabled && features::kBackupRefPtrModeParam.Get() != - features::BackupRefPtrMode::kDisabled) + features::BackupRefPtrMode::kDisabled) { brp_nondefault_behavior = true; + } if (brp_finch_enabled && features::kBackupRefPtrModeParam.Get() == - features::BackupRefPtrMode::kEnabled) + features::BackupRefPtrMode::kEnabled) { brp_truly_enabled = true; + } #endif // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) [[maybe_unused]] bool pcscan_enabled = #if defined(PA_ALLOW_PCSCAN) @@ -397,8 +428,9 @@ void DanglingRawPtrDetected(uintptr_t id) { internal::PartitionAutoLock guard(g_stack_trace_buffer_lock); #if DCHECK_IS_ON() - for (absl::optional& entry : g_stack_trace_buffer) + for (absl::optional& entry : g_stack_trace_buffer) { PA_DCHECK(!entry || entry->id != id); + } #endif // DCHECK_IS_ON() for (absl::optional& entry : g_stack_trace_buffer) { @@ -598,5 +630,506 @@ void InstallUnretainedDanglingRawPtrChecks() { } } -} // namespace allocator -} // namespace base +namespace { + +void SetProcessNameForPCScan(const std::string& process_type) { + const char* name = [&process_type] { + if (process_type.empty()) { + // Empty means browser process. + return "Browser"; + } + if (process_type == switches::kRendererProcess) { + return "Renderer"; + } + if (process_type == switches::kGpuProcess) { + return "Gpu"; + } + if (process_type == switches::kUtilityProcess) { + return "Utility"; + } + return static_cast(nullptr); + }(); + + if (name) { + partition_alloc::internal::PCScan::SetProcessName(name); + } +} + +bool EnablePCScanForMallocPartitionsIfNeeded() { +#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN) + using Config = partition_alloc::internal::PCScan::InitConfig; + DCHECK(base::FeatureList::GetInstance()); + if (base::FeatureList::IsEnabled(base::features::kPartitionAllocPCScan)) { + allocator_shim::EnablePCScan({Config::WantedWriteProtectionMode::kEnabled, + Config::SafepointMode::kEnabled}); + base::allocator::RegisterPCScanStatsReporter(); + return true; + } +#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN) + return false; +} + +bool EnablePCScanForMallocPartitionsInBrowserProcessIfNeeded() { +#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN) + using Config = partition_alloc::internal::PCScan::InitConfig; + DCHECK(base::FeatureList::GetInstance()); + if (base::FeatureList::IsEnabled( + base::features::kPartitionAllocPCScanBrowserOnly)) { + const Config::WantedWriteProtectionMode wp_mode = + base::FeatureList::IsEnabled(base::features::kPartitionAllocDCScan) + ? Config::WantedWriteProtectionMode::kEnabled + : Config::WantedWriteProtectionMode::kDisabled; +#if !defined(PA_STARSCAN_UFFD_WRITE_PROTECTOR_SUPPORTED) + CHECK_EQ(Config::WantedWriteProtectionMode::kDisabled, wp_mode) + << "DCScan is currently only supported on Linux based systems"; +#endif + allocator_shim::EnablePCScan({wp_mode, Config::SafepointMode::kEnabled}); + base::allocator::RegisterPCScanStatsReporter(); + return true; + } +#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN) + return false; +} + +bool EnablePCScanForMallocPartitionsInRendererProcessIfNeeded() { +#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN) + using Config = partition_alloc::internal::PCScan::InitConfig; + DCHECK(base::FeatureList::GetInstance()); + if (base::FeatureList::IsEnabled( + base::features::kPartitionAllocPCScanRendererOnly)) { + const Config::WantedWriteProtectionMode wp_mode = + base::FeatureList::IsEnabled(base::features::kPartitionAllocDCScan) + ? Config::WantedWriteProtectionMode::kEnabled + : Config::WantedWriteProtectionMode::kDisabled; +#if !defined(PA_STARSCAN_UFFD_WRITE_PROTECTOR_SUPPORTED) + CHECK_EQ(Config::WantedWriteProtectionMode::kDisabled, wp_mode) + << "DCScan is currently only supported on Linux based systems"; +#endif + allocator_shim::EnablePCScan({wp_mode, Config::SafepointMode::kDisabled}); + base::allocator::RegisterPCScanStatsReporter(); + return true; + } +#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN) + return false; +} + +} // namespace + +void ReconfigurePartitionForKnownProcess(const std::string& process_type) { + DCHECK_NE(process_type, switches::kZygoteProcess); + // TODO(keishi): Move the code to enable BRP back here after Finch + // experiments. +} + +PartitionAllocSupport::PartitionAllocSupport() = default; + +void PartitionAllocSupport::ReconfigureEarlyish( + const std::string& process_type) { + { + base::AutoLock scoped_lock(lock_); + // TODO(bartekn): Switch to DCHECK once confirmed there are no issues. + CHECK(!called_earlyish_) + << "ReconfigureEarlyish was already called for process '" + << established_process_type_ << "'; current process: '" << process_type + << "'"; + + called_earlyish_ = true; + established_process_type_ = process_type; + } + + if (process_type != switches::kZygoteProcess) { + ReconfigurePartitionForKnownProcess(process_type); + } + + // These initializations are only relevant for PartitionAlloc-Everywhere + // builds. +#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) + allocator_shim::EnablePartitionAllocMemoryReclaimer(); +#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) +} + +void PartitionAllocSupport::ReconfigureAfterZygoteFork( + const std::string& process_type) { + { + base::AutoLock scoped_lock(lock_); + // TODO(bartekn): Switch to DCHECK once confirmed there are no issues. + CHECK(!called_after_zygote_fork_) + << "ReconfigureAfterZygoteFork was already called for process '" + << established_process_type_ << "'; current process: '" << process_type + << "'"; + DCHECK(called_earlyish_) + << "Attempt to call ReconfigureAfterZygoteFork without calling " + "ReconfigureEarlyish; current process: '" + << process_type << "'"; + DCHECK_EQ(established_process_type_, switches::kZygoteProcess) + << "Attempt to call ReconfigureAfterZygoteFork while " + "ReconfigureEarlyish was called on non-zygote process '" + << established_process_type_ << "'; current process: '" << process_type + << "'"; + + called_after_zygote_fork_ = true; + established_process_type_ = process_type; + } + + if (process_type != switches::kZygoteProcess) { + ReconfigurePartitionForKnownProcess(process_type); + } +} + +void PartitionAllocSupport::ReconfigureAfterFeatureListInit( + const std::string& process_type) { + base::allocator::InstallDanglingRawPtrChecks(); + base::allocator::InstallUnretainedDanglingRawPtrChecks(); + { + base::AutoLock scoped_lock(lock_); + // Avoid initializing more than once. + // TODO(bartekn): See if can be converted to (D)CHECK. + if (called_after_feature_list_init_) { + DCHECK_EQ(established_process_type_, process_type) + << "ReconfigureAfterFeatureListInit was already called for process '" + << established_process_type_ << "'; current process: '" + << process_type << "'"; + return; + } + DCHECK(called_earlyish_) + << "Attempt to call ReconfigureAfterFeatureListInit without calling " + "ReconfigureEarlyish; current process: '" + << process_type << "'"; + DCHECK_NE(established_process_type_, switches::kZygoteProcess) + << "Attempt to call ReconfigureAfterFeatureListInit without calling " + "ReconfigureAfterZygoteFork; current process: '" + << process_type << "'"; + DCHECK_EQ(established_process_type_, process_type) + << "ReconfigureAfterFeatureListInit wasn't called for an already " + "established process '" + << established_process_type_ << "'; current process: '" << process_type + << "'"; + + called_after_feature_list_init_ = true; + } + + DCHECK_NE(process_type, switches::kZygoteProcess); + // TODO(bartekn): Switch to DCHECK once confirmed there are no issues. + CHECK(base::FeatureList::GetInstance()); + + bool enable_brp = false; + [[maybe_unused]] bool enable_brp_zapping = false; + [[maybe_unused]] bool split_main_partition = false; + [[maybe_unused]] bool use_dedicated_aligned_partition = false; + [[maybe_unused]] bool add_dummy_ref_count = false; + [[maybe_unused]] bool process_affected_by_brp_flag = false; + +#if (BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \ + BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)) || \ + BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) + if (base::FeatureList::IsEnabled( + base::features::kPartitionAllocBackupRefPtr)) { + // No specified process type means this is the Browser process. + switch (base::features::kBackupRefPtrEnabledProcessesParam.Get()) { + case base::features::BackupRefPtrEnabledProcesses::kBrowserOnly: + process_affected_by_brp_flag = process_type.empty(); + break; + case base::features::BackupRefPtrEnabledProcesses::kBrowserAndRenderer: + process_affected_by_brp_flag = + process_type.empty() || + (process_type == switches::kRendererProcess); + break; + case base::features::BackupRefPtrEnabledProcesses::kNonRenderer: + process_affected_by_brp_flag = + (process_type != switches::kRendererProcess); + break; + case base::features::BackupRefPtrEnabledProcesses::kAllProcesses: + process_affected_by_brp_flag = true; + break; + } + } +#endif // (BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && + // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)) || + // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) + +#if BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) + if (process_affected_by_brp_flag) { + base::RawPtrAsanService::GetInstance().Configure( + base::EnableDereferenceCheck( + base::features::kBackupRefPtrAsanEnableDereferenceCheckParam.Get()), + base::EnableExtractionCheck( + base::features::kBackupRefPtrAsanEnableExtractionCheckParam.Get()), + base::EnableInstantiationCheck( + base::features::kBackupRefPtrAsanEnableInstantiationCheckParam + .Get())); + } else { + base::RawPtrAsanService::GetInstance().Configure( + base::EnableDereferenceCheck(false), base::EnableExtractionCheck(false), + base::EnableInstantiationCheck(false)); + } +#endif // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) + +#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \ + BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) + // TODO(keishi): BRP experiment temporarily disabled on Mac releases during + // Finch freeze. +#if !BUILDFLAG(IS_MAC) || !defined(OFFICIAL_BUILD) + if (process_affected_by_brp_flag) { + switch (base::features::kBackupRefPtrModeParam.Get()) { + case base::features::BackupRefPtrMode::kDisabled: + // Do nothing. Equivalent to !IsEnabled(kPartitionAllocBackupRefPtr). + break; + + case base::features::BackupRefPtrMode::kEnabled: + enable_brp_zapping = true; + ABSL_FALLTHROUGH_INTENDED; + case base::features::BackupRefPtrMode::kEnabledWithoutZapping: + enable_brp = true; + split_main_partition = true; +#if !BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) + // AlignedAlloc relies on natural alignment offered by the allocator + // (see the comment inside PartitionRoot::AlignedAllocFlags). Any extras + // in front of the allocation will mess up that alignment. Such extras + // are used when BackupRefPtr is on, in which case, we need a separate + // partition, dedicated to handle only aligned allocations, where those + // extras are disabled. However, if the "previous slot" variant is used, + // no dedicated partition is needed, as the extras won't interfere with + // the alignment requirements. + use_dedicated_aligned_partition = true; +#endif + break; + + case base::features::BackupRefPtrMode::kDisabledButSplitPartitions2Way: + split_main_partition = true; + break; + + case base::features::BackupRefPtrMode::kDisabledButSplitPartitions3Way: + split_main_partition = true; + use_dedicated_aligned_partition = true; + break; + + case base::features::BackupRefPtrMode::kDisabledButAddDummyRefCount: + split_main_partition = true; + add_dummy_ref_count = true; +#if !BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) + use_dedicated_aligned_partition = true; +#endif + break; + } + } +#endif // !BUILDFLAG(IS_MAC) || !defined(OFFICIAL_BUILD) +#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && + // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) + +#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) + allocator_shim::ConfigurePartitions( + allocator_shim::EnableBrp(enable_brp), + allocator_shim::EnableBrpZapping(enable_brp_zapping), + allocator_shim::SplitMainPartition(split_main_partition), + allocator_shim::UseDedicatedAlignedPartition( + use_dedicated_aligned_partition), + allocator_shim::AddDummyRefCount(add_dummy_ref_count), + allocator_shim::AlternateBucketDistribution( + base::features::kPartitionAllocAlternateBucketDistributionParam + .Get())); +#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) + + // If BRP is not enabled, check if any of PCScan flags is enabled. + bool scan_enabled = false; + if (!enable_brp) { + scan_enabled = EnablePCScanForMallocPartitionsIfNeeded(); + // No specified process type means this is the Browser process. + if (process_type.empty()) { + scan_enabled = scan_enabled || + EnablePCScanForMallocPartitionsInBrowserProcessIfNeeded(); + } + if (process_type == switches::kRendererProcess) { + scan_enabled = scan_enabled || + EnablePCScanForMallocPartitionsInRendererProcessIfNeeded(); + } + if (scan_enabled) { + if (base::FeatureList::IsEnabled( + base::features::kPartitionAllocPCScanStackScanning)) { +#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) + partition_alloc::internal::PCScan::EnableStackScanning(); + // Notify PCScan about the main thread. + partition_alloc::internal::PCScan::NotifyThreadCreated( + partition_alloc::internal::GetStackTop()); +#endif + } + if (base::FeatureList::IsEnabled( + base::features::kPartitionAllocPCScanImmediateFreeing)) { + partition_alloc::internal::PCScan::EnableImmediateFreeing(); + } + if (base::FeatureList::IsEnabled( + base::features::kPartitionAllocPCScanEagerClearing)) { + partition_alloc::internal::PCScan::SetClearType( + partition_alloc::internal::PCScan::ClearType::kEager); + } + SetProcessNameForPCScan(process_type); + } + } + +#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) + // Non-quarantinable partition is dealing with hot V8's zone allocations. + // In case PCScan is enabled in Renderer, enable thread cache on this + // partition. At the same time, thread cache on the main(malloc) partition + // must be disabled, because only one partition can have it on. + if (scan_enabled && process_type == switches::kRendererProcess) { + base::internal::NonQuarantinableAllocator::Instance() + .root() + ->EnableThreadCacheIfSupported(); + } else { + allocator_shim::internal::PartitionAllocMalloc::Allocator() + ->EnableThreadCacheIfSupported(); + } + + if (base::FeatureList::IsEnabled( + base::features::kPartitionAllocLargeEmptySlotSpanRing)) { + allocator_shim::internal::PartitionAllocMalloc::Allocator() + ->EnableLargeEmptySlotSpanRing(); + allocator_shim::internal::PartitionAllocMalloc::AlignedAllocator() + ->EnableLargeEmptySlotSpanRing(); + } +#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) + +#if BUILDFLAG(IS_WIN) + // Browser process only, since this is the one we want to prevent from + // crashing the most (as it takes down all the tabs). + if (base::FeatureList::IsEnabled( + base::features::kPageAllocatorRetryOnCommitFailure) && + process_type.empty()) { + partition_alloc::SetRetryOnCommitFailure(true); + } +#endif +} + +void PartitionAllocSupport::ReconfigureAfterTaskRunnerInit( + const std::string& process_type) { + { + base::AutoLock scoped_lock(lock_); + + // Init only once. + if (called_after_thread_pool_init_) { + return; + } + + DCHECK_EQ(established_process_type_, process_type); + // Enforce ordering. + DCHECK(called_earlyish_); + DCHECK(called_after_feature_list_init_); + + called_after_thread_pool_init_ = true; + } + +#if defined(PA_THREAD_CACHE_SUPPORTED) && \ + BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) + // This should be called in specific processes, as the main thread is + // initialized later. + DCHECK(process_type != switches::kZygoteProcess); + + base::allocator::StartThreadCachePeriodicPurge(); + +#if BUILDFLAG(IS_ANDROID) + // Lower thread cache limits to avoid stranding too much memory in the caches. + if (base::SysInfo::IsLowEndDevice()) { + ::partition_alloc::ThreadCacheRegistry::Instance().SetThreadCacheMultiplier( + ::partition_alloc::ThreadCache::kDefaultMultiplier / 2.); + } +#endif // BUILDFLAG(IS_ANDROID) + + // Renderer processes are more performance-sensitive, increase thread cache + // limits. + if (process_type == switches::kRendererProcess && + base::FeatureList::IsEnabled( + base::features::kPartitionAllocLargeThreadCacheSize)) { + largest_cached_size_ = + ::partition_alloc::ThreadCacheLimits::kLargeSizeThreshold; + +#if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_32_BITS) + // Devices almost always report less physical memory than what they actually + // have, so anything above 3GiB will catch 4GiB and above. + if (base::SysInfo::AmountOfPhysicalMemoryMB() <= 3500) { + largest_cached_size_ = + ::partition_alloc::ThreadCacheLimits::kDefaultSizeThreshold; + } +#endif // BUILDFLAG(IS_ANDROID) && !defined(ARCH_CPU_64_BITS) + + ::partition_alloc::ThreadCache::SetLargestCachedSize(largest_cached_size_); + } + +#endif // defined(PA_THREAD_CACHE_SUPPORTED) && + // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) + + if (base::FeatureList::IsEnabled( + base::features::kPartitionAllocPCScanMUAwareScheduler)) { + // Assign PCScan a task-based scheduling backend. + static base::NoDestructor< + partition_alloc::internal::MUAwareTaskBasedBackend> + mu_aware_task_based_backend{ + partition_alloc::internal::PCScan::scheduler(), + &partition_alloc::internal::PCScan::PerformDelayedScan}; + partition_alloc::internal::PCScan::scheduler().SetNewSchedulingBackend( + *mu_aware_task_based_backend.get()); + } + +#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) + base::allocator::StartMemoryReclaimer( + base::SingleThreadTaskRunner::GetCurrentDefault()); +#endif + + if (base::FeatureList::IsEnabled( + base::features::kPartitionAllocSortActiveSlotSpans)) { + partition_alloc::PartitionRoot< + partition_alloc::internal::ThreadSafe>::EnableSortActiveSlotSpans(); + } +} + +void PartitionAllocSupport::OnForegrounded(bool has_main_frame) { +#if defined(PA_THREAD_CACHE_SUPPORTED) && \ + BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) + { + base::AutoLock scoped_lock(lock_); + if (established_process_type_ != switches::kRendererProcess) { + return; + } + } + + if (!base::FeatureList::IsEnabled( + features::kLowerPAMemoryLimitForNonMainRenderers) || + has_main_frame) { + ::partition_alloc::ThreadCache::SetLargestCachedSize(largest_cached_size_); + } +#endif // defined(PA_THREAD_CACHE_SUPPORTED) && + // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) +} + +void PartitionAllocSupport::OnBackgrounded() { +#if defined(PA_THREAD_CACHE_SUPPORTED) && \ + BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) + { + base::AutoLock scoped_lock(lock_); + if (established_process_type_ != switches::kRendererProcess) { + return; + } + } + + // Performance matters less for background renderers, don't pay the memory + // cost. + ::partition_alloc::ThreadCache::SetLargestCachedSize( + ::partition_alloc::ThreadCacheLimits::kDefaultSizeThreshold); + + // In renderers, memory reclaim uses the "idle time" task runner to run + // periodic reclaim. This does not always run when the renderer is idle, and + // in particular after the renderer gets backgrounded. As a result, empty slot + // spans are potentially never decommitted. To mitigate that, run a one-off + // reclaim a few seconds later. Even if the renderer comes back to foreground + // in the meantime, the worst case is a few more system calls. + // + // TODO(lizeb): Remove once/if the behavior of idle tasks changes. + base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( + FROM_HERE, base::BindOnce([]() { + ::partition_alloc::MemoryReclaimer::Instance()->ReclaimAll(); + }), + base::Seconds(10)); + +#endif // defined(PA_THREAD_CACHE_SUPPORTED) && + // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) +} + +} // namespace base::allocator diff --git a/base/allocator/partition_alloc_support.h b/base/allocator/partition_alloc_support.h index 3a747e2a426bd..fa0ce9c97c502 100644 --- a/base/allocator/partition_alloc_support.h +++ b/base/allocator/partition_alloc_support.h @@ -8,13 +8,16 @@ #include #include +#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" #include "base/allocator/partition_allocator/partition_alloc_config.h" +#include "base/allocator/partition_allocator/thread_cache.h" #include "base/base_export.h" #include "base/memory/scoped_refptr.h" +#include "base/synchronization/lock.h" #include "base/task/sequenced_task_runner.h" +#include "base/thread_annotations.h" -namespace base { -namespace allocator { +namespace base::allocator { #if defined(PA_ALLOW_PCSCAN) BASE_EXPORT void RegisterPCScanStatsReporter(); @@ -37,7 +40,63 @@ BASE_EXPORT std::map ProposeSyntheticFinchTrials(); BASE_EXPORT void InstallDanglingRawPtrChecks(); BASE_EXPORT void InstallUnretainedDanglingRawPtrChecks(); -} // namespace allocator -} // namespace base +// Allows to re-configure PartitionAlloc at run-time. +class BASE_EXPORT PartitionAllocSupport { + public: + // Reconfigure* functions re-configure PartitionAlloc. It is impossible to + // configure PartitionAlloc before/at its initialization using information not + // known at compile-time (e.g. process type, Finch), because by the time this + // information is available memory allocations would have surely happened, + // that requiring a functioning allocator. + // + // *Earlyish() is called as early as it is reasonably possible. + // *AfterZygoteFork() is its complement to finish configuring process-specific + // stuff that had to be postponed due to *Earlyish() being called with + // |process_type==kZygoteProcess|. + // *AfterFeatureListInit() is called in addition to the above, once + // FeatureList has been initialized and ready to use. It is guaranteed to be + // called on non-zygote processes or after the zygote has been forked. + // *AfterTaskRunnerInit() is called once it is possible to post tasks, and + // after the previous steps. + // + // *Earlyish() must be called exactly once. *AfterZygoteFork() must be called + // once iff *Earlyish() was called before with |process_type==kZygoteProcess|. + // + // *AfterFeatureListInit() may be called more than once, but will perform its + // re-configuration steps exactly once. + // + // *AfterTaskRunnerInit() may be called more than once. + void ReconfigureEarlyish(const std::string& process_type); + void ReconfigureAfterZygoteFork(const std::string& process_type); + void ReconfigureAfterFeatureListInit(const std::string& process_type); + void ReconfigureAfterTaskRunnerInit(const std::string& process_type); + + // |has_main_frame| tells us if the renderer contains a main frame. + void OnForegrounded(bool has_main_frame); + void OnBackgrounded(); + + static PartitionAllocSupport* Get() { + static auto* singleton = new PartitionAllocSupport(); + return singleton; + } + + private: + PartitionAllocSupport(); + + base::Lock lock_; + bool called_earlyish_ GUARDED_BY(lock_) = false; + bool called_after_zygote_fork_ GUARDED_BY(lock_) = false; + bool called_after_feature_list_init_ GUARDED_BY(lock_) = false; + bool called_after_thread_pool_init_ GUARDED_BY(lock_) = false; + std::string established_process_type_ GUARDED_BY(lock_) = "INVALID"; + +#if defined(PA_THREAD_CACHE_SUPPORTED) && \ + BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) + size_t largest_cached_size_ = + ::partition_alloc::ThreadCacheLimits::kDefaultSizeThreshold; +#endif +}; + +} // namespace base::allocator #endif // BASE_ALLOCATOR_PARTITION_ALLOC_SUPPORT_H_ diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc index eeae42302c35e..3b3603723fdce 100644 --- a/content/app/content_main_runner_impl.cc +++ b/content/app/content_main_runner_impl.cc @@ -15,6 +15,7 @@ #include #include "base/allocator/allocator_check.h" +#include "base/allocator/partition_alloc_support.h" #include "base/at_exit.h" #include "base/base_switches.h" #include "base/bind.h" @@ -69,7 +70,6 @@ #include "content/common/android/cpu_time_metrics.h" #include "content/common/content_constants_internal.h" #include "content/common/mojo_core_library_support.h" -#include "content/common/partition_alloc_support.h" #include "content/common/process_visibility_tracker.h" #include "content/common/url_schemes.h" #include "content/gpu/in_process_gpu_thread.h" @@ -648,7 +648,7 @@ int NO_STACK_PROTECTOR RunZygote(ContentMainDelegate* delegate) { std::string process_type = command_line->GetSwitchValueASCII(switches::kProcessType); - internal::PartitionAllocSupport::Get()->ReconfigureAfterZygoteFork( + base::allocator::PartitionAllocSupport::Get()->ReconfigureAfterZygoteFork( process_type); CreateChildThreadPool(process_type); @@ -669,8 +669,8 @@ int NO_STACK_PROTECTOR RunZygote(ContentMainDelegate* delegate) { delegate->PostEarlyInitialization( ContentMainDelegate::InvokedInChildProcess()); - internal::PartitionAllocSupport::Get()->ReconfigureAfterFeatureListInit( - process_type); + base::allocator::PartitionAllocSupport::Get() + ->ReconfigureAfterFeatureListInit(process_type); for (size_t i = 0; i < std::size(kMainFunctions); ++i) { if (process_type == kMainFunctions[i].name) @@ -859,7 +859,8 @@ int ContentMainRunnerImpl::Initialize(ContentMainParams params) { std::string process_type = command_line.GetSwitchValueASCII(switches::kProcessType); - internal::PartitionAllocSupport::Get()->ReconfigureEarlyish(process_type); + base::allocator::PartitionAllocSupport::Get()->ReconfigureEarlyish( + process_type); #if BUILDFLAG(IS_WIN) if (command_line.HasSwitch(switches::kDeviceScaleFactor)) { @@ -1064,8 +1065,8 @@ int NO_STACK_PROTECTOR ContentMainRunnerImpl::Run() { delegate_->PostEarlyInitialization( ContentMainDelegate::InvokedInChildProcess()); - internal::PartitionAllocSupport::Get()->ReconfigureAfterFeatureListInit( - process_type); + base::allocator::PartitionAllocSupport::Get() + ->ReconfigureAfterFeatureListInit(process_type); } #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) @@ -1237,8 +1238,10 @@ int ContentMainRunnerImpl::RunBrowser(MainFunctionParams main_params, } // No specified process type means this is the Browser process. - internal::PartitionAllocSupport::Get()->ReconfigureAfterFeatureListInit(""); - internal::PartitionAllocSupport::Get()->ReconfigureAfterTaskRunnerInit(""); + base::allocator::PartitionAllocSupport::Get() + ->ReconfigureAfterFeatureListInit(""); + base::allocator::PartitionAllocSupport::Get()->ReconfigureAfterTaskRunnerInit( + ""); if (start_minimal_browser) { DVLOG(0) << "Chrome is running in minimal browser mode."; diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index bd3571770e45c..9780ad5ac6d8f 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -111,7 +111,6 @@ #include "content/browser/webui/web_ui_impl.h" #include "content/browser/xr/service/xr_runtime_manager_impl.h" #include "content/common/content_switches_internal.h" -#include "content/common/partition_alloc_support.h" #include "content/public/browser/ax_inspect_factory.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_plugin_guest_manager.h" diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn index 54df36f673af1..bc96005c80cec 100644 --- a/content/common/BUILD.gn +++ b/content/common/BUILD.gn @@ -149,8 +149,6 @@ source_set("common") { "navigation_gesture.h", "navigation_params_utils.h", "origin_util.cc", - "partition_alloc_support.cc", - "partition_alloc_support.h", "private_aggregation_features.cc", "private_aggregation_features.h", "process_type.cc", diff --git a/content/common/OWNERS b/content/common/OWNERS index c5d1cb6a750fe..bfde526a32507 100644 --- a/content/common/OWNERS +++ b/content/common/OWNERS @@ -29,5 +29,3 @@ per-file *font_config_ipc_linux*=file://ipc/SECURITY_OWNERS per-file *.mojom=set noparent per-file *.mojom=file://ipc/SECURITY_OWNERS - -per-file partition_alloc_support.*=file://base/allocator/partition_allocator/OWNERS diff --git a/content/common/partition_alloc_support.cc b/content/common/partition_alloc_support.cc deleted file mode 100644 index 16c1ff2742a8c..0000000000000 --- a/content/common/partition_alloc_support.cc +++ /dev/null @@ -1,539 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/common/partition_alloc_support.h" - -#include - -#include "base/allocator/partition_alloc_features.h" -#include "base/allocator/partition_alloc_support.h" -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/shim/allocator_shim.h" -#include "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h" -#include "base/allocator/partition_allocator/starscan/pcscan.h" -#include "base/allocator/partition_allocator/starscan/pcscan_scheduling.h" -#include "base/allocator/partition_allocator/starscan/stack/stack.h" -#include "base/allocator/partition_allocator/thread_cache.h" -#include "base/bind.h" -#include "base/callback.h" -#include "base/feature_list.h" -#include "base/location.h" -#include "base/memory/nonscannable_memory.h" -#include "base/memory/raw_ptr_asan_service.h" -#include "base/no_destructor.h" -#include "base/task/single_thread_task_runner.h" -#include "base/time/time.h" -#include "build/build_config.h" -#include "content/public/common/content_features.h" -#include "content/public/common/content_switches.h" - -#if BUILDFLAG(IS_ANDROID) -#include "base/system/sys_info.h" -#endif - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -#include "base/allocator/partition_allocator/memory_reclaimer.h" -#endif - -namespace content { -namespace internal { - -namespace { - -void SetProcessNameForPCScan(const std::string& process_type) { - const char* name = [&process_type] { - if (process_type.empty()) { - // Empty means browser process. - return "Browser"; - } - if (process_type == switches::kRendererProcess) - return "Renderer"; - if (process_type == switches::kGpuProcess) - return "Gpu"; - if (process_type == switches::kUtilityProcess) - return "Utility"; - return static_cast(nullptr); - }(); - - if (name) { - partition_alloc::internal::PCScan::SetProcessName(name); - } -} - -bool EnablePCScanForMallocPartitionsIfNeeded() { -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN) - using Config = partition_alloc::internal::PCScan::InitConfig; - DCHECK(base::FeatureList::GetInstance()); - if (base::FeatureList::IsEnabled(base::features::kPartitionAllocPCScan)) { - allocator_shim::EnablePCScan({Config::WantedWriteProtectionMode::kEnabled, - Config::SafepointMode::kEnabled}); - base::allocator::RegisterPCScanStatsReporter(); - return true; - } -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN) - return false; -} - -bool EnablePCScanForMallocPartitionsInBrowserProcessIfNeeded() { -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN) - using Config = partition_alloc::internal::PCScan::InitConfig; - DCHECK(base::FeatureList::GetInstance()); - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocPCScanBrowserOnly)) { - const Config::WantedWriteProtectionMode wp_mode = - base::FeatureList::IsEnabled(base::features::kPartitionAllocDCScan) - ? Config::WantedWriteProtectionMode::kEnabled - : Config::WantedWriteProtectionMode::kDisabled; -#if !defined(PA_STARSCAN_UFFD_WRITE_PROTECTOR_SUPPORTED) - CHECK_EQ(Config::WantedWriteProtectionMode::kDisabled, wp_mode) - << "DCScan is currently only supported on Linux based systems"; -#endif - allocator_shim::EnablePCScan({wp_mode, Config::SafepointMode::kEnabled}); - base::allocator::RegisterPCScanStatsReporter(); - return true; - } -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN) - return false; -} - -bool EnablePCScanForMallocPartitionsInRendererProcessIfNeeded() { -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN) - using Config = partition_alloc::internal::PCScan::InitConfig; - DCHECK(base::FeatureList::GetInstance()); - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocPCScanRendererOnly)) { - const Config::WantedWriteProtectionMode wp_mode = - base::FeatureList::IsEnabled(base::features::kPartitionAllocDCScan) - ? Config::WantedWriteProtectionMode::kEnabled - : Config::WantedWriteProtectionMode::kDisabled; -#if !defined(PA_STARSCAN_UFFD_WRITE_PROTECTOR_SUPPORTED) - CHECK_EQ(Config::WantedWriteProtectionMode::kDisabled, wp_mode) - << "DCScan is currently only supported on Linux based systems"; -#endif - allocator_shim::EnablePCScan({wp_mode, Config::SafepointMode::kDisabled}); - base::allocator::RegisterPCScanStatsReporter(); - return true; - } -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN) - return false; -} - -} // namespace - -void ReconfigurePartitionForKnownProcess(const std::string& process_type) { - DCHECK_NE(process_type, switches::kZygoteProcess); - // TODO(keishi): Move the code to enable BRP back here after Finch - // experiments. -} - -PartitionAllocSupport::PartitionAllocSupport() = default; - -void PartitionAllocSupport::ReconfigureEarlyish( - const std::string& process_type) { - { - base::AutoLock scoped_lock(lock_); - // TODO(bartekn): Switch to DCHECK once confirmed there are no issues. - CHECK(!called_earlyish_) - << "ReconfigureEarlyish was already called for process '" - << established_process_type_ << "'; current process: '" << process_type - << "'"; - - called_earlyish_ = true; - established_process_type_ = process_type; - } - - if (process_type != switches::kZygoteProcess) { - ReconfigurePartitionForKnownProcess(process_type); - } - - // These initializations are only relevant for PartitionAlloc-Everywhere - // builds. -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - allocator_shim::EnablePartitionAllocMemoryReclaimer(); -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -} - -void PartitionAllocSupport::ReconfigureAfterZygoteFork( - const std::string& process_type) { - { - base::AutoLock scoped_lock(lock_); - // TODO(bartekn): Switch to DCHECK once confirmed there are no issues. - CHECK(!called_after_zygote_fork_) - << "ReconfigureAfterZygoteFork was already called for process '" - << established_process_type_ << "'; current process: '" << process_type - << "'"; - DCHECK(called_earlyish_) - << "Attempt to call ReconfigureAfterZygoteFork without calling " - "ReconfigureEarlyish; current process: '" - << process_type << "'"; - DCHECK_EQ(established_process_type_, switches::kZygoteProcess) - << "Attempt to call ReconfigureAfterZygoteFork while " - "ReconfigureEarlyish was called on non-zygote process '" - << established_process_type_ << "'; current process: '" << process_type - << "'"; - - called_after_zygote_fork_ = true; - established_process_type_ = process_type; - } - - if (process_type != switches::kZygoteProcess) { - ReconfigurePartitionForKnownProcess(process_type); - } -} - -void PartitionAllocSupport::ReconfigureAfterFeatureListInit( - const std::string& process_type) { - base::allocator::InstallDanglingRawPtrChecks(); - base::allocator::InstallUnretainedDanglingRawPtrChecks(); - { - base::AutoLock scoped_lock(lock_); - // Avoid initializing more than once. - // TODO(bartekn): See if can be converted to (D)CHECK. - if (called_after_feature_list_init_) { - DCHECK_EQ(established_process_type_, process_type) - << "ReconfigureAfterFeatureListInit was already called for process '" - << established_process_type_ << "'; current process: '" - << process_type << "'"; - return; - } - DCHECK(called_earlyish_) - << "Attempt to call ReconfigureAfterFeatureListInit without calling " - "ReconfigureEarlyish; current process: '" - << process_type << "'"; - DCHECK_NE(established_process_type_, switches::kZygoteProcess) - << "Attempt to call ReconfigureAfterFeatureListInit without calling " - "ReconfigureAfterZygoteFork; current process: '" - << process_type << "'"; - DCHECK_EQ(established_process_type_, process_type) - << "ReconfigureAfterFeatureListInit wasn't called for an already " - "established process '" - << established_process_type_ << "'; current process: '" << process_type - << "'"; - - called_after_feature_list_init_ = true; - } - - DCHECK_NE(process_type, switches::kZygoteProcess); - // TODO(bartekn): Switch to DCHECK once confirmed there are no issues. - CHECK(base::FeatureList::GetInstance()); - - bool enable_brp = false; - [[maybe_unused]] bool enable_brp_zapping = false; - [[maybe_unused]] bool split_main_partition = false; - [[maybe_unused]] bool use_dedicated_aligned_partition = false; - [[maybe_unused]] bool add_dummy_ref_count = false; - [[maybe_unused]] bool process_affected_by_brp_flag = false; - -#if (BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \ - BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)) || \ - BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocBackupRefPtr)) { - // No specified process type means this is the Browser process. - switch (base::features::kBackupRefPtrEnabledProcessesParam.Get()) { - case base::features::BackupRefPtrEnabledProcesses::kBrowserOnly: - process_affected_by_brp_flag = process_type.empty(); - break; - case base::features::BackupRefPtrEnabledProcesses::kBrowserAndRenderer: - process_affected_by_brp_flag = - process_type.empty() || - (process_type == switches::kRendererProcess); - break; - case base::features::BackupRefPtrEnabledProcesses::kNonRenderer: - process_affected_by_brp_flag = - (process_type != switches::kRendererProcess); - break; - case base::features::BackupRefPtrEnabledProcesses::kAllProcesses: - process_affected_by_brp_flag = true; - break; - } - } -#endif // (BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && - // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)) || - // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) - -#if BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) - if (process_affected_by_brp_flag) { - base::RawPtrAsanService::GetInstance().Configure( - base::EnableDereferenceCheck( - base::features::kBackupRefPtrAsanEnableDereferenceCheckParam.Get()), - base::EnableExtractionCheck( - base::features::kBackupRefPtrAsanEnableExtractionCheckParam.Get()), - base::EnableInstantiationCheck( - base::features::kBackupRefPtrAsanEnableInstantiationCheckParam - .Get())); - } else { - base::RawPtrAsanService::GetInstance().Configure( - base::EnableDereferenceCheck(false), base::EnableExtractionCheck(false), - base::EnableInstantiationCheck(false)); - } -#endif // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \ - BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - // TODO(keishi): BRP experiment temporarily disabled on Mac releases during - // Finch freeze. -#if !BUILDFLAG(IS_MAC) || !defined(OFFICIAL_BUILD) - if (process_affected_by_brp_flag) { - switch (base::features::kBackupRefPtrModeParam.Get()) { - case base::features::BackupRefPtrMode::kDisabled: - // Do nothing. Equivalent to !IsEnabled(kPartitionAllocBackupRefPtr). - break; - - case base::features::BackupRefPtrMode::kEnabled: - enable_brp_zapping = true; - ABSL_FALLTHROUGH_INTENDED; - case base::features::BackupRefPtrMode::kEnabledWithoutZapping: - enable_brp = true; - split_main_partition = true; -#if !BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - // AlignedAlloc relies on natural alignment offered by the allocator - // (see the comment inside PartitionRoot::AlignedAllocFlags). Any extras - // in front of the allocation will mess up that alignment. Such extras - // are used when BackupRefPtr is on, in which case, we need a separate - // partition, dedicated to handle only aligned allocations, where those - // extras are disabled. However, if the "previous slot" variant is used, - // no dedicated partition is needed, as the extras won't interfere with - // the alignment requirements. - use_dedicated_aligned_partition = true; -#endif - break; - - case base::features::BackupRefPtrMode::kDisabledButSplitPartitions2Way: - split_main_partition = true; - break; - - case base::features::BackupRefPtrMode::kDisabledButSplitPartitions3Way: - split_main_partition = true; - use_dedicated_aligned_partition = true; - break; - - case base::features::BackupRefPtrMode::kDisabledButAddDummyRefCount: - split_main_partition = true; - add_dummy_ref_count = true; -#if !BUILDFLAG(PUT_REF_COUNT_IN_PREVIOUS_SLOT) - use_dedicated_aligned_partition = true; -#endif - break; - } - } -#endif // !BUILDFLAG(IS_MAC) || !defined(OFFICIAL_BUILD) -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && - // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT) - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - allocator_shim::ConfigurePartitions( - allocator_shim::EnableBrp(enable_brp), - allocator_shim::EnableBrpZapping(enable_brp_zapping), - allocator_shim::SplitMainPartition(split_main_partition), - allocator_shim::UseDedicatedAlignedPartition( - use_dedicated_aligned_partition), - allocator_shim::AddDummyRefCount(add_dummy_ref_count), - allocator_shim::AlternateBucketDistribution( - base::features::kPartitionAllocAlternateBucketDistributionParam - .Get())); -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - - // If BRP is not enabled, check if any of PCScan flags is enabled. - bool scan_enabled = false; - if (!enable_brp) { - scan_enabled = EnablePCScanForMallocPartitionsIfNeeded(); - // No specified process type means this is the Browser process. - if (process_type.empty()) { - scan_enabled = scan_enabled || - EnablePCScanForMallocPartitionsInBrowserProcessIfNeeded(); - } - if (process_type == switches::kRendererProcess) { - scan_enabled = scan_enabled || - EnablePCScanForMallocPartitionsInRendererProcessIfNeeded(); - } - if (scan_enabled) { - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocPCScanStackScanning)) { -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - partition_alloc::internal::PCScan::EnableStackScanning(); - // Notify PCScan about the main thread. - partition_alloc::internal::PCScan::NotifyThreadCreated( - partition_alloc::internal::GetStackTop()); -#endif - } - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocPCScanImmediateFreeing)) { - partition_alloc::internal::PCScan::EnableImmediateFreeing(); - } - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocPCScanEagerClearing)) { - partition_alloc::internal::PCScan::SetClearType( - partition_alloc::internal::PCScan::ClearType::kEager); - } - SetProcessNameForPCScan(process_type); - } - } - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - // Non-quarantinable partition is dealing with hot V8's zone allocations. - // In case PCScan is enabled in Renderer, enable thread cache on this - // partition. At the same time, thread cache on the main(malloc) partition - // must be disabled, because only one partition can have it on. - if (scan_enabled && process_type == switches::kRendererProcess) { - base::internal::NonQuarantinableAllocator::Instance() - .root() - ->EnableThreadCacheIfSupported(); - } else { - allocator_shim::internal::PartitionAllocMalloc::Allocator() - ->EnableThreadCacheIfSupported(); - } - - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocLargeEmptySlotSpanRing)) { - allocator_shim::internal::PartitionAllocMalloc::Allocator() - ->EnableLargeEmptySlotSpanRing(); - allocator_shim::internal::PartitionAllocMalloc::AlignedAllocator() - ->EnableLargeEmptySlotSpanRing(); - } -#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - -#if BUILDFLAG(IS_WIN) - // Browser process only, since this is the one we want to prevent from - // crashing the most (as it takes down all the tabs). - if (base::FeatureList::IsEnabled( - base::features::kPageAllocatorRetryOnCommitFailure) && - process_type.empty()) { - partition_alloc::SetRetryOnCommitFailure(true); - } -#endif -} - -void PartitionAllocSupport::ReconfigureAfterTaskRunnerInit( - const std::string& process_type) { - { - base::AutoLock scoped_lock(lock_); - - // Init only once. - if (called_after_thread_pool_init_) - return; - - DCHECK_EQ(established_process_type_, process_type); - // Enforce ordering. - DCHECK(called_earlyish_); - DCHECK(called_after_feature_list_init_); - - called_after_thread_pool_init_ = true; - } - -#if defined(PA_THREAD_CACHE_SUPPORTED) && \ - BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - // This should be called in specific processes, as the main thread is - // initialized later. - DCHECK(process_type != switches::kZygoteProcess); - - base::allocator::StartThreadCachePeriodicPurge(); - -#if BUILDFLAG(IS_ANDROID) - // Lower thread cache limits to avoid stranding too much memory in the caches. - if (base::SysInfo::IsLowEndDevice()) { - ::partition_alloc::ThreadCacheRegistry::Instance().SetThreadCacheMultiplier( - ::partition_alloc::ThreadCache::kDefaultMultiplier / 2.); - } -#endif // BUILDFLAG(IS_ANDROID) - - // Renderer processes are more performance-sensitive, increase thread cache - // limits. - if (process_type == switches::kRendererProcess && - base::FeatureList::IsEnabled( - base::features::kPartitionAllocLargeThreadCacheSize)) { - largest_cached_size_ = - ::partition_alloc::ThreadCacheLimits::kLargeSizeThreshold; - -#if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_32_BITS) - // Devices almost always report less physical memory than what they actually - // have, so anything above 3GiB will catch 4GiB and above. - if (base::SysInfo::AmountOfPhysicalMemoryMB() <= 3500) - largest_cached_size_ = - ::partition_alloc::ThreadCacheLimits::kDefaultSizeThreshold; -#endif // BUILDFLAG(IS_ANDROID) && !defined(ARCH_CPU_64_BITS) - - ::partition_alloc::ThreadCache::SetLargestCachedSize(largest_cached_size_); - } - -#endif // defined(PA_THREAD_CACHE_SUPPORTED) && - // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocPCScanMUAwareScheduler)) { - // Assign PCScan a task-based scheduling backend. - static base::NoDestructor< - partition_alloc::internal::MUAwareTaskBasedBackend> - mu_aware_task_based_backend{ - partition_alloc::internal::PCScan::scheduler(), - &partition_alloc::internal::PCScan::PerformDelayedScan}; - partition_alloc::internal::PCScan::scheduler().SetNewSchedulingBackend( - *mu_aware_task_based_backend.get()); - } - -#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - base::allocator::StartMemoryReclaimer( - base::SingleThreadTaskRunner::GetCurrentDefault()); -#endif - - if (base::FeatureList::IsEnabled( - base::features::kPartitionAllocSortActiveSlotSpans)) { - partition_alloc::PartitionRoot< - partition_alloc::internal::ThreadSafe>::EnableSortActiveSlotSpans(); - } -} - -void PartitionAllocSupport::OnForegrounded(bool has_main_frame) { -#if defined(PA_THREAD_CACHE_SUPPORTED) && \ - BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - { - base::AutoLock scoped_lock(lock_); - if (established_process_type_ != switches::kRendererProcess) - return; - } - - if (!base::FeatureList::IsEnabled( - features::kLowerPAMemoryLimitForNonMainRenderers) || - has_main_frame) - ::partition_alloc::ThreadCache::SetLargestCachedSize(largest_cached_size_); -#endif // defined(PA_THREAD_CACHE_SUPPORTED) && - // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -} - -void PartitionAllocSupport::OnBackgrounded() { -#if defined(PA_THREAD_CACHE_SUPPORTED) && \ - BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - { - base::AutoLock scoped_lock(lock_); - if (established_process_type_ != switches::kRendererProcess) - return; - } - - // Performance matters less for background renderers, don't pay the memory - // cost. - ::partition_alloc::ThreadCache::SetLargestCachedSize( - ::partition_alloc::ThreadCacheLimits::kDefaultSizeThreshold); - - // In renderers, memory reclaim uses the "idle time" task runner to run - // periodic reclaim. This does not always run when the renderer is idle, and - // in particular after the renderer gets backgrounded. As a result, empty slot - // spans are potentially never decommitted. To mitigate that, run a one-off - // reclaim a few seconds later. Even if the renderer comes back to foreground - // in the meantime, the worst case is a few more system calls. - // - // TODO(lizeb): Remove once/if the behavior of idle tasks changes. - base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( - FROM_HERE, base::BindOnce([]() { - ::partition_alloc::MemoryReclaimer::Instance()->ReclaimAll(); - }), - base::Seconds(10)); - -#endif // defined(PA_THREAD_CACHE_SUPPORTED) && - // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) -} - -} // namespace internal -} // namespace content diff --git a/content/common/partition_alloc_support.h b/content/common/partition_alloc_support.h deleted file mode 100644 index f331f45365d98..0000000000000 --- a/content/common/partition_alloc_support.h +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2021 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_COMMON_PARTITION_ALLOC_SUPPORT_H_ -#define CONTENT_COMMON_PARTITION_ALLOC_SUPPORT_H_ - -#include - -#include "base/allocator/partition_allocator/partition_alloc_buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc_config.h" -#include "base/allocator/partition_allocator/thread_cache.h" -#include "base/synchronization/lock.h" -#include "base/thread_annotations.h" - -namespace content { -namespace internal { - -// Allows to re-configure PartitionAlloc at run-time. -class PartitionAllocSupport { - public: - // Reconfigure* functions re-configure PartitionAlloc. It is impossible to - // configure PartitionAlloc before/at its initialization using information not - // known at compile-time (e.g. process type, Finch), because by the time this - // information is available memory allocations would have surely happened, - // that requiring a functioning allocator. - // - // *Earlyish() is called as early as it is reasonably possible. - // *AfterZygoteFork() is its complement to finish configuring process-specific - // stuff that had to be postponed due to *Earlyish() being called with - // |process_type==kZygoteProcess|. - // *AfterFeatureListInit() is called in addition to the above, once - // FeatureList has been initialized and ready to use. It is guaranteed to be - // called on non-zygote processes or after the zygote has been forked. - // *AfterTaskRunnerInit() is called once it is possible to post tasks, and - // after the previous steps. - // - // *Earlyish() must be called exactly once. *AfterZygoteFork() must be called - // once iff *Earlyish() was called before with |process_type==kZygoteProcess|. - // - // *AfterFeatureListInit() may be called more than once, but will perform its - // re-configuration steps exactly once. - // - // *AfterTaskRunnerInit() may be called more than once. - void ReconfigureEarlyish(const std::string& process_type); - void ReconfigureAfterZygoteFork(const std::string& process_type); - void ReconfigureAfterFeatureListInit(const std::string& process_type); - void ReconfigureAfterTaskRunnerInit(const std::string& process_type); - - // |has_main_frame| tells us if the renderer contains a main frame. - void OnForegrounded(bool has_main_frame); - void OnBackgrounded(); - - static PartitionAllocSupport* Get() { - static auto* singleton = new PartitionAllocSupport(); - return singleton; - } - - private: - PartitionAllocSupport(); - - base::Lock lock_; - bool called_earlyish_ GUARDED_BY(lock_) = false; - bool called_after_zygote_fork_ GUARDED_BY(lock_) = false; - bool called_after_feature_list_init_ GUARDED_BY(lock_) = false; - bool called_after_thread_pool_init_ GUARDED_BY(lock_) = false; - std::string established_process_type_ GUARDED_BY(lock_) = "INVALID"; - -#if defined(PA_THREAD_CACHE_SUPPORTED) && \ - BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) - size_t largest_cached_size_ = - ::partition_alloc::ThreadCacheLimits::kDefaultSizeThreshold; -#endif -}; - -} // namespace internal -} // namespace content - -#endif // CONTENT_COMMON_PARTITION_ALLOC_SUPPORT_H_ diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc index a044de5f13ef0..ca1886385f297 100644 --- a/content/gpu/gpu_main.cc +++ b/content/gpu/gpu_main.cc @@ -9,6 +9,7 @@ #include #include +#include "base/allocator/partition_alloc_support.h" #include "base/bind.h" #include "base/check.h" #include "base/command_line.h" @@ -35,7 +36,6 @@ #include "content/child/child_process.h" #include "content/common/content_constants_internal.h" #include "content/common/content_switches_internal.h" -#include "content/common/partition_alloc_support.h" #include "content/common/skia_utils.h" #include "content/gpu/gpu_child_thread.h" #include "content/public/common/content_client.h" @@ -389,7 +389,7 @@ int GpuMain(MainFunctionParams parameters) { nullptr); #endif - internal::PartitionAllocSupport::Get()->ReconfigureAfterTaskRunnerInit( + base::allocator::PartitionAllocSupport::Get()->ReconfigureAfterTaskRunnerInit( switches::kGpuProcess); base::HighResolutionTimerManager hi_res_timer_manager; diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc index f9ca058c9641e..3c718fd0951b1 100644 --- a/content/public/common/content_features.cc +++ b/content/public/common/content_features.cc @@ -621,9 +621,6 @@ BASE_FEATURE(kLogJsConsoleMessages, // Configures whether we set a lower limit for renderers that do not have a main // frame, similar to the limit that is already done for backgrounded renderers. -BASE_FEATURE(kLowerPAMemoryLimitForNonMainRenderers, - "LowerPAMemoryLimitForNonMainRenderers", - base::FEATURE_DISABLED_BY_DEFAULT); BASE_FEATURE(kLowerV8MemoryLimitForNonMainRenderers, "LowerV8MemoryLimitForNonMainRenderers", base::FEATURE_DISABLED_BY_DEFAULT); diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index 2ac91dcba1d7a..1ab2dbae16a7d 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc @@ -13,6 +13,7 @@ #include #include "base/allocator/allocator_extension.h" +#include "base/allocator/partition_alloc_support.h" #include "base/at_exit.h" #include "base/bind.h" #include "base/callback_helpers.h" @@ -72,7 +73,6 @@ #include "content/common/content_constants_internal.h" #include "content/common/content_switches_internal.h" #include "content/common/main_frame_counter.h" -#include "content/common/partition_alloc_support.h" #include "content/common/process_visibility_tracker.h" #include "content/common/pseudonymization_salt.h" #include "content/public/common/content_constants.h" @@ -1759,13 +1759,13 @@ bool RenderThreadImpl::RendererIsBackgrounded() const { void RenderThreadImpl::OnRendererBackgrounded() { main_thread_scheduler_->SetRendererBackgrounded(true); discardable_memory_allocator_->OnBackgrounded(); - internal::PartitionAllocSupport::Get()->OnBackgrounded(); + base::allocator::PartitionAllocSupport::Get()->OnBackgrounded(); } void RenderThreadImpl::OnRendererForegrounded() { main_thread_scheduler_->SetRendererBackgrounded(false); discardable_memory_allocator_->OnForegrounded(); - internal::PartitionAllocSupport::Get()->OnForegrounded( + base::allocator::PartitionAllocSupport::Get()->OnForegrounded( MainFrameCounter::has_main_frame()); process_foregrounded_count_++; } diff --git a/content/renderer/renderer_main.cc b/content/renderer/renderer_main.cc index adb052ddbee2b..3dc2fe6c90acd 100644 --- a/content/renderer/renderer_main.cc +++ b/content/renderer/renderer_main.cc @@ -5,6 +5,7 @@ #include #include +#include "base/allocator/partition_alloc_support.h" #include "base/base_switches.h" #include "base/command_line.h" #include "base/debug/debugger.h" @@ -28,7 +29,6 @@ #include "build/chromeos_buildflags.h" #include "content/common/content_constants_internal.h" #include "content/common/content_switches_internal.h" -#include "content/common/partition_alloc_support.h" #include "content/common/skia_utils.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" @@ -312,8 +312,8 @@ int RendererMain(MainFunctionParams parameters) { mojo::BeginRandomMojoDelays(); #endif - internal::PartitionAllocSupport::Get()->ReconfigureAfterTaskRunnerInit( - switches::kRendererProcess); + base::allocator::PartitionAllocSupport::Get() + ->ReconfigureAfterTaskRunnerInit(switches::kRendererProcess); base::HighResolutionTimerManager hi_res_timer_manager; diff --git a/content/utility/utility_main.cc b/content/utility/utility_main.cc index bf924f4b5f45e..6986bf1485ecb 100644 --- a/content/utility/utility_main.cc +++ b/content/utility/utility_main.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/allocator/partition_alloc_support.h" #include "base/bind.h" #include "base/command_line.h" #include "base/debug/leak_annotations.h" @@ -16,7 +17,6 @@ #include "components/services/screen_ai/buildflags/buildflags.h" #include "content/child/child_process.h" #include "content/common/content_switches_internal.h" -#include "content/common/partition_alloc_support.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" @@ -299,7 +299,7 @@ int UtilityMain(MainFunctionParams parameters) { } #endif - internal::PartitionAllocSupport::Get()->ReconfigureAfterTaskRunnerInit( + base::allocator::PartitionAllocSupport::Get()->ReconfigureAfterTaskRunnerInit( switches::kUtilityProcess); run_loop.Run();