1 // Copyright 2021 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_STARSCAN_PCSCAN_INTERNAL_H_ 6 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_STARSCAN_PCSCAN_INTERNAL_H_ 7 8 #include <array> 9 #include <functional> 10 #include <memory> 11 #include <mutex> 12 #include <unordered_map> 13 #include <utility> 14 #include <vector> 15 16 #include "base/allocator/partition_allocator/partition_alloc_base/memory/scoped_refptr.h" 17 #include "base/allocator/partition_allocator/partition_alloc_base/no_destructor.h" 18 #include "base/allocator/partition_allocator/starscan/metadata_allocator.h" 19 #include "base/allocator/partition_allocator/starscan/pcscan.h" 20 #include "base/allocator/partition_allocator/starscan/starscan_fwd.h" 21 #include "base/allocator/partition_allocator/starscan/write_protector.h" 22 23 namespace partition_alloc::internal { 24 25 class PCScanTask; 26 27 // Internal PCScan singleton. The separation between frontend and backend is 28 // needed to keep access to the hot data (quarantine) in the frontend fast, 29 // whereas the backend can hold cold data. 30 class PCScanInternal final { 31 public: 32 using Root = PCScan::Root; 33 using TaskHandle = scoped_refptr<PCScanTask>; 34 35 using SuperPages = std::vector<uintptr_t, MetadataAllocator<uintptr_t>>; 36 using RootsMap = 37 std::unordered_map<Root*, 38 SuperPages, 39 std::hash<Root*>, 40 std::equal_to<>, 41 MetadataAllocator<std::pair<Root* const, SuperPages>>>; 42 Instance()43 static PCScanInternal& Instance() { 44 // Since the data that PCScanInternal holds is cold, it's fine to have the 45 // runtime check for thread-safe local static initialization. 46 static internal::base::NoDestructor<PCScanInternal> instance; 47 return *instance; 48 } 49 50 PCScanInternal(const PCScanInternal&) = delete; 51 PCScanInternal& operator=(const PCScanInternal&) = delete; 52 53 ~PCScanInternal(); 54 55 void Initialize(PCScan::InitConfig); is_initialized()56 bool is_initialized() const { return is_initialized_; } 57 58 void PerformScan(PCScan::InvocationMode); 59 void PerformScanIfNeeded(PCScan::InvocationMode); 60 void PerformDelayedScan(base::TimeDelta delay); 61 void JoinScan(); 62 63 TaskHandle CurrentPCScanTask() const; 64 void SetCurrentPCScanTask(TaskHandle task); 65 void ResetCurrentPCScanTask(); 66 67 void RegisterScannableRoot(Root*); 68 void RegisterNonScannableRoot(Root*); 69 scannable_roots()70 RootsMap& scannable_roots() { return scannable_roots_; } scannable_roots()71 const RootsMap& scannable_roots() const { return scannable_roots_; } 72 nonscannable_roots()73 RootsMap& nonscannable_roots() { return nonscannable_roots_; } nonscannable_roots()74 const RootsMap& nonscannable_roots() const { return nonscannable_roots_; } 75 76 void RegisterNewSuperPage(Root* root, uintptr_t super_page_base); 77 78 void SetProcessName(const char* name); process_name()79 const char* process_name() const { return process_name_; } 80 81 // Get size of all committed pages from scannable and nonscannable roots. 82 size_t CalculateTotalHeapSize() const; 83 simd_support()84 SimdSupport simd_support() const { return simd_support_; } 85 86 void EnableStackScanning(); 87 void DisableStackScanning(); 88 bool IsStackScanningEnabled() const; 89 EnableImmediateFreeing()90 void EnableImmediateFreeing() { immediate_freeing_enabled_ = true; } IsImmediateFreeingEnabled()91 bool IsImmediateFreeingEnabled() const { return immediate_freeing_enabled_; } 92 93 void NotifyThreadCreated(void* stack_top); 94 void NotifyThreadDestroyed(); 95 96 void* GetCurrentThreadStackTop() const; 97 98 bool WriteProtectionEnabled() const; 99 void ProtectPages(uintptr_t begin, size_t size); 100 void UnprotectPages(uintptr_t begin, size_t size); 101 102 void ClearRootsForTesting(); // IN-TEST 103 void ReinitForTesting(PCScan::InitConfig); // IN-TEST 104 void FinishScanForTesting(); // IN-TEST 105 106 void RegisterStatsReporter(partition_alloc::StatsReporter* reporter); 107 partition_alloc::StatsReporter& GetReporter(); 108 109 private: 110 friend internal::base::NoDestructor<PCScanInternal>; 111 friend class StarScanSnapshot; 112 113 using StackTops = std::unordered_map< 114 internal::base::PlatformThreadId, 115 void*, 116 std::hash<internal::base::PlatformThreadId>, 117 std::equal_to<>, 118 MetadataAllocator< 119 std::pair<const internal::base::PlatformThreadId, void*>>>; 120 121 PCScanInternal(); 122 123 TaskHandle current_task_; 124 mutable std::mutex current_task_mutex_; 125 126 RootsMap scannable_roots_; 127 RootsMap nonscannable_roots_; 128 mutable std::mutex roots_mutex_; 129 130 bool stack_scanning_enabled_{false}; 131 // TLS emulation of stack tops. Since this is guaranteed to go through 132 // non-quarantinable partition, using it from safepoints is safe. 133 StackTops stack_tops_; 134 mutable std::mutex stack_tops_mutex_; 135 136 bool immediate_freeing_enabled_{false}; 137 138 const char* process_name_ = nullptr; 139 const SimdSupport simd_support_; 140 141 std::unique_ptr<WriteProtector> write_protector_; 142 partition_alloc::StatsReporter* stats_reporter_ = nullptr; 143 144 bool is_initialized_ = false; 145 }; 146 147 } // namespace partition_alloc::internal 148 149 #endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_STARSCAN_PCSCAN_INTERNAL_H_ 150