| // 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 BASE_ALLOCATOR_PARTITION_ALLOC_SUPPORT_H_ |
| #define BASE_ALLOCATOR_PARTITION_ALLOC_SUPPORT_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| |
| #include "base/allocator/scheduler_loop_quarantine_config.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/task/single_thread_task_runner.h" |
| #include "base/thread_annotations.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "partition_alloc/buildflags.h" |
| #include "partition_alloc/partition_alloc_config.h" |
| |
| #if PA_BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(IS_POSIX) |
| #include "base/memory/weak_ptr.h" |
| #endif |
| |
| #if PA_CONFIG(THREAD_CACHE_SUPPORTED) && \ |
| PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) |
| #include "partition_alloc/partition_alloc_constants.h" |
| #endif |
| |
| namespace base { |
| class LockMetricTagList; |
| } |
| |
| namespace base::allocator { |
| |
| BASE_EXPORT const LockMetricTagList& GetPartitionAllocLockMetricTagList(); |
| |
| // Starts a periodic timer on the current thread to purge all thread caches. |
| BASE_EXPORT void StartThreadCachePeriodicPurge(); |
| |
| BASE_EXPORT void StartMemoryReclaimer( |
| scoped_refptr<SequencedTaskRunner> task_runner); |
| |
| BASE_EXPORT std::map<std::string, std::string> ProposeSyntheticFinchTrials(); |
| |
| // Install handlers for when dangling raw_ptr(s) have been detected. This prints |
| // two StackTraces. One where the memory is freed, one where the last dangling |
| // raw_ptr stopped referencing it. |
| // |
| // This is currently effective, only when compiled with |
| // `enable_dangling_raw_ptr_checks` build flag. |
| BASE_EXPORT void InstallDanglingRawPtrChecks(); |
| BASE_EXPORT void InstallUnretainedDanglingRawPtrChecks(); |
| |
| // Once called, makes `free()` do nothing. This is done to reduce |
| // shutdown hangs on CrOS. |
| // Does nothing if Dangling Pointer Detector (`docs/dangling_ptr.md`) |
| // is not active. |
| // Does nothing if allocator shim support is not built. |
| BASE_EXPORT void MakeFreeNoOp(); |
| |
| // Checks if the scheduler loop quarantine feature is enabled for the given |
| // process type. |
| BASE_EXPORT bool IsSchedulerLoopQuarantineEnabled( |
| std::string_view process_type); |
| // Apply specialized configuration to the quarantine branch for the current |
| // thread. |
| BASE_EXPORT void ReconfigureSchedulerLoopQuarantineBranch( |
| SchedulerLoopQuarantineBranchType branch_type); |
| |
| // Returns a "process type identifier" for the current process. |
| // It is the process type (e.g. "renderer", "gpu-process") for non-utility |
| // processes. For the browser process, it is "browser". |
| // For utility processes, it is "utility" followed by "." and the utility |
| // sub-type name (e.g. "utility.network.mojom.NetworkService"). |
| // |
| // Must be called after `CommandLine::Init()`. |
| BASE_EXPORT std::string GetProcessTypeIdentifier(); |
| |
| // Configuration for `ReconfigureAfterFeatureListInit()`. |
| struct BASE_EXPORT FeatureListConfiguration { |
| bool configure_dangling_pointer_detector = true; |
| bool is_in_death_test_child = false; |
| }; |
| |
| // Allows to re-configure PartitionAlloc at run-time. |
| class BASE_EXPORT PartitionAllocSupport { |
| public: |
| struct BrpConfiguration { |
| bool enable_brp = false; |
| |
| // TODO(https://crbug.com/371135823): Remove after the investigation. |
| size_t extra_extras_size = 0; |
| bool suppress_double_free_detected_crash = false; |
| bool suppress_corruption_detected_crash = false; |
| }; |
| |
| // 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 ReconfigureForTests(); |
| void ReconfigureEarlyish(std::string_view process_type); |
| void ReconfigureAfterZygoteFork(std::string_view process_type); |
| void ReconfigureAfterFeatureListInit(std::string_view process_type, |
| FeatureListConfiguration config = {}); |
| void ReconfigureAfterTaskRunnerInit(std::string_view process_type); |
| |
| // |has_main_frame| tells us if the renderer contains a main frame. |
| // The default value is intended for other process types, where the parameter |
| // does not make sense. |
| void OnForegrounded(bool has_main_frame = false); |
| void OnBackgrounded(); |
| |
| #if PA_BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) |
| static std::string ExtractDanglingPtrSignatureForTests( |
| std::string_view stacktrace); |
| #endif |
| |
| static PartitionAllocSupport* Get(); |
| |
| static BrpConfiguration GetBrpConfiguration(std::string_view process_type); |
| |
| // Returns true if memory tagging should be enabled if available for the given |
| // process type. May be called multiple times per process. |
| static bool ShouldEnableMemoryTagging(std::string_view process_type); |
| |
| // For calling from within third_party/blink/. |
| static bool ShouldEnableMemoryTaggingInRendererProcess(); |
| |
| // Returns true if PA advanced checks should be enabled if available for the |
| // given process type. May be called multiple times per process. |
| static bool ShouldEnablePartitionAllocWithAdvancedChecks( |
| std::string_view process_type); |
| |
| #if PA_BUILDFLAG(HAS_64_BIT_POINTERS) |
| enum class MemoryPressureLevel { |
| kNoPressure, |
| kCriticalPressure, |
| }; |
| |
| using AsyncSafeMemoryPressureNotification = void (*)(); |
| |
| // Provide a callback to notify of a memory pressure event, which can be |
| // queried with `GetMemoryPressureLevel()`. The callback *must* be async-safe, |
| // as it's typically invoked from within malloc(), so any memory allocation |
| // performed within will deadlock. Notifications do not block allocations, so |
| // crashing due to memory pressure is still possible, though the |
| // implementation tries to dispatch it early enough. |
| static void SetAsyncSafeMemoryPressureNotification( |
| AsyncSafeMemoryPressureNotification notification); |
| // Returns the current memory pressure level. |
| static MemoryPressureLevel GetMemoryPressureLevel(); |
| #endif // PA_BUILDFLAG(HAS_64_BIT_POINTERS) |
| |
| private: |
| PartitionAllocSupport(); |
| |
| base::Lock lock_; |
| bool called_for_tests_ GUARDED_BY(lock_) = false; |
| 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 PA_CONFIG(THREAD_CACHE_SUPPORTED) && \ |
| PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) |
| size_t largest_cached_size_ = |
| ::partition_alloc::kThreadCacheDefaultSizeThreshold; |
| #endif |
| }; |
| |
| #if PA_BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(IS_POSIX) |
| // Visible in header for testing. |
| class BASE_EXPORT PartitionAllocMemoryPressureMonitor { |
| public: |
| static PartitionAllocMemoryPressureMonitor& Instance(); |
| PartitionAllocMemoryPressureMonitor(); |
| ~PartitionAllocMemoryPressureMonitor(); |
| |
| void Start(scoped_refptr<SingleThreadTaskRunner> main_thread_task_runner); |
| void ResetForTesting(); |
| |
| private: |
| struct WatcherState; |
| |
| static void OnPartitionAllocPressureFromMalloc(); |
| void MaybeNotifyMemoryPressureOnMainThread(); |
| |
| static void StartWatchingOnThreadPool( |
| WatcherState* state, |
| scoped_refptr<SingleThreadTaskRunner> main_thread_task_runner, |
| base::WeakPtr<PartitionAllocMemoryPressureMonitor> weak_this); |
| static void OnPipeReadable( |
| int read_fd, |
| scoped_refptr<SingleThreadTaskRunner> main_thread_task_runner, |
| base::WeakPtr<PartitionAllocMemoryPressureMonitor> weak_this); |
| |
| scoped_refptr<SequencedTaskRunner> watcher_task_runner_; |
| std::unique_ptr<WatcherState> watcher_state_; |
| |
| PartitionAllocSupport::MemoryPressureLevel last_notified_level_ = |
| PartitionAllocSupport::MemoryPressureLevel::kNoPressure; |
| base::WeakPtrFactory<PartitionAllocMemoryPressureMonitor> weak_factory_{this}; |
| }; |
| #endif // PA_BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(IS_POSIX) |
| |
| // Visible in header for testing. |
| class BASE_EXPORT MemoryReclaimerSupport { |
| public: |
| static MemoryReclaimerSupport& Instance(); |
| MemoryReclaimerSupport(); |
| ~MemoryReclaimerSupport(); |
| void Start(scoped_refptr<TaskRunner> task_runner); |
| void SetForegrounded(bool in_foreground); |
| |
| void ResetForTesting(); |
| bool has_pending_task_for_testing() const { return has_pending_task_; } |
| static TimeDelta GetInterval(); |
| |
| // Visible for testing |
| static constexpr base::TimeDelta kFirstPAPurgeOrReclaimDelay = |
| base::Minutes(1); |
| |
| private: |
| void Run(); |
| void MaybeScheduleTask(TimeDelta delay = TimeDelta()); |
| |
| scoped_refptr<TaskRunner> task_runner_; |
| bool in_foreground_ = true; |
| bool has_pending_task_ = false; |
| }; |
| |
| // The function here is called right before crashing with |
| // `DoubleFreeOrCorruptionDetected()`. We provide an address for the slot start |
| // to the function, and it may use that for debugging purpose. |
| BASE_EXPORT void SetDoubleFreeOrCorruptionDetectedFn(void (*fn)(uintptr_t)); |
| |
| } // namespace base::allocator |
| |
| #endif // BASE_ALLOCATOR_PARTITION_ALLOC_SUPPORT_H_ |