• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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