• 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_STATS_COLLECTOR_H_
6 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_STARSCAN_STATS_COLLECTOR_H_
7 
8 #include <array>
9 #include <atomic>
10 #include <functional>
11 #include <mutex>
12 #include <string>
13 #include <type_traits>
14 #include <unordered_map>
15 #include <utility>
16 
17 #include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h"
18 #include "base/allocator/partition_allocator/partition_alloc_base/time/time.h"
19 #include "base/allocator/partition_allocator/starscan/metadata_allocator.h"
20 #include "base/allocator/partition_allocator/starscan/starscan_fwd.h"
21 
22 namespace partition_alloc {
23 
24 class StatsReporter;
25 
26 namespace internal {
27 
28 #define FOR_ALL_PCSCAN_SCANNER_SCOPES(V) \
29   V(Clear)                               \
30   V(Scan)                                \
31   V(Sweep)                               \
32   V(Overall)
33 
34 #define FOR_ALL_PCSCAN_MUTATOR_SCOPES(V) \
35   V(Clear)                               \
36   V(ScanStack)                           \
37   V(Scan)                                \
38   V(Overall)
39 
40 class StatsCollector final {
41  public:
42   enum class ScannerId {
43 #define DECLARE_ENUM(name) k##name,
44     FOR_ALL_PCSCAN_SCANNER_SCOPES(DECLARE_ENUM)
45 #undef DECLARE_ENUM
46         kNumIds,
47   };
48 
49   enum class MutatorId {
50 #define DECLARE_ENUM(name) k##name,
51     FOR_ALL_PCSCAN_MUTATOR_SCOPES(DECLARE_ENUM)
52 #undef DECLARE_ENUM
53         kNumIds,
54   };
55 
56   template <Context context>
57   using IdType =
58       std::conditional_t<context == Context::kMutator, MutatorId, ScannerId>;
59 
60   // We don't immediately trace events, but instead defer it until scanning is
61   // done. This is needed to avoid unpredictable work that can be done by traces
62   // (e.g. recursive mutex lock).
63   struct DeferredTraceEvent {
64     base::TimeTicks start_time;
65     base::TimeTicks end_time;
66   };
67 
68   // Thread-safe hash-map that maps thread id to scanner events. Doesn't
69   // accumulate events, i.e. every event can only be registered once.
70   template <Context context>
71   class DeferredTraceEventMap final {
72    public:
73     using IdType = StatsCollector::IdType<context>;
74     using PerThreadEvents =
75         std::array<DeferredTraceEvent, static_cast<size_t>(IdType::kNumIds)>;
76     using UnderlyingMap = std::unordered_map<
77         internal::base::PlatformThreadId,
78         PerThreadEvents,
79         std::hash<internal::base::PlatformThreadId>,
80         std::equal_to<>,
81         MetadataAllocator<std::pair<const internal::base::PlatformThreadId,
82                                     PerThreadEvents>>>;
83 
84     inline void RegisterBeginEventFromCurrentThread(IdType id);
85     inline void RegisterEndEventFromCurrentThread(IdType id);
86 
get_underlying_map_unsafe()87     const UnderlyingMap& get_underlying_map_unsafe() const { return events_; }
88 
89    private:
90     std::mutex mutex_;
91     UnderlyingMap events_;
92   };
93 
94   template <Context context>
95   class Scope final {
96    public:
Scope(StatsCollector & stats,IdType<context> type)97     Scope(StatsCollector& stats, IdType<context> type)
98         : stats_(stats), type_(type) {
99       stats_.RegisterBeginEventFromCurrentThread(type);
100     }
101 
102     Scope(const Scope&) = delete;
103     Scope& operator=(const Scope&) = delete;
104 
~Scope()105     ~Scope() { stats_.RegisterEndEventFromCurrentThread(type_); }
106 
107    private:
108     StatsCollector& stats_;
109     IdType<context> type_;
110   };
111 
112   using ScannerScope = Scope<Context::kScanner>;
113   using MutatorScope = Scope<Context::kMutator>;
114 
115   StatsCollector(const char* process_name, size_t quarantine_last_size);
116 
117   StatsCollector(const StatsCollector&) = delete;
118   StatsCollector& operator=(const StatsCollector&) = delete;
119 
120   ~StatsCollector();
121 
IncreaseSurvivedQuarantineSize(size_t size)122   void IncreaseSurvivedQuarantineSize(size_t size) {
123     survived_quarantine_size_.fetch_add(size, std::memory_order_relaxed);
124   }
survived_quarantine_size()125   size_t survived_quarantine_size() const {
126     return survived_quarantine_size_.load(std::memory_order_relaxed);
127   }
128 
IncreaseSweptSize(size_t size)129   void IncreaseSweptSize(size_t size) { swept_size_ += size; }
swept_size()130   size_t swept_size() const { return swept_size_; }
131 
IncreaseDiscardedQuarantineSize(size_t size)132   void IncreaseDiscardedQuarantineSize(size_t size) {
133     discarded_quarantine_size_ += size;
134   }
135 
136   base::TimeDelta GetOverallTime() const;
137   void ReportTracesAndHists(partition_alloc::StatsReporter& reporter) const;
138 
139  private:
140   using MetadataString =
141       std::basic_string<char, std::char_traits<char>, MetadataAllocator<char>>;
142 
143   MetadataString ToUMAString(ScannerId id) const;
144   MetadataString ToUMAString(MutatorId id) const;
145 
RegisterBeginEventFromCurrentThread(MutatorId id)146   void RegisterBeginEventFromCurrentThread(MutatorId id) {
147     mutator_trace_events_.RegisterBeginEventFromCurrentThread(id);
148   }
RegisterEndEventFromCurrentThread(MutatorId id)149   void RegisterEndEventFromCurrentThread(MutatorId id) {
150     mutator_trace_events_.RegisterEndEventFromCurrentThread(id);
151   }
RegisterBeginEventFromCurrentThread(ScannerId id)152   void RegisterBeginEventFromCurrentThread(ScannerId id) {
153     scanner_trace_events_.RegisterBeginEventFromCurrentThread(id);
154   }
RegisterEndEventFromCurrentThread(ScannerId id)155   void RegisterEndEventFromCurrentThread(ScannerId id) {
156     scanner_trace_events_.RegisterEndEventFromCurrentThread(id);
157   }
158 
159   template <Context context>
160   base::TimeDelta GetTimeImpl(const DeferredTraceEventMap<context>& event_map,
161                               IdType<context> id) const;
162 
163   template <Context context>
164   void ReportTracesAndHistsImpl(
165       partition_alloc::StatsReporter& reporter,
166       const DeferredTraceEventMap<context>& event_map) const;
167 
168   void ReportSurvivalRate(partition_alloc::StatsReporter& reporter) const;
169 
170   DeferredTraceEventMap<Context::kMutator> mutator_trace_events_;
171   DeferredTraceEventMap<Context::kScanner> scanner_trace_events_;
172 
173   std::atomic<size_t> survived_quarantine_size_{0u};
174   size_t swept_size_ = 0u;
175   size_t discarded_quarantine_size_ = 0u;
176   const char* process_name_ = nullptr;
177   const size_t quarantine_last_size_ = 0u;
178 };
179 
180 template <Context context>
181 inline void StatsCollector::DeferredTraceEventMap<
RegisterBeginEventFromCurrentThread(IdType id)182     context>::RegisterBeginEventFromCurrentThread(IdType id) {
183   std::lock_guard<std::mutex> lock(mutex_);
184   const auto tid = base::PlatformThread::CurrentId();
185   const auto now = base::TimeTicks::Now();
186   auto& event_array = events_[tid];
187   auto& event = event_array[static_cast<size_t>(id)];
188   PA_DCHECK(event.start_time.is_null());
189   PA_DCHECK(event.end_time.is_null());
190   event.start_time = now;
191 }
192 
193 template <Context context>
194 inline void StatsCollector::DeferredTraceEventMap<
RegisterEndEventFromCurrentThread(IdType id)195     context>::RegisterEndEventFromCurrentThread(IdType id) {
196   std::lock_guard<std::mutex> lock(mutex_);
197   const auto tid = base::PlatformThread::CurrentId();
198   const auto now = base::TimeTicks::Now();
199   auto& event_array = events_[tid];
200   auto& event = event_array[static_cast<size_t>(id)];
201   PA_DCHECK(!event.start_time.is_null());
202   PA_DCHECK(event.end_time.is_null());
203   event.end_time = now;
204 }
205 
ToUMAString(ScannerId id)206 inline StatsCollector::MetadataString StatsCollector::ToUMAString(
207     ScannerId id) const {
208   PA_DCHECK(process_name_);
209   const MetadataString process_name = process_name_;
210   switch (id) {
211     case ScannerId::kClear:
212       return "PA.PCScan." + process_name + ".Scanner.Clear";
213     case ScannerId::kScan:
214       return "PA.PCScan." + process_name + ".Scanner.Scan";
215     case ScannerId::kSweep:
216       return "PA.PCScan." + process_name + ".Scanner.Sweep";
217     case ScannerId::kOverall:
218       return "PA.PCScan." + process_name + ".Scanner";
219     case ScannerId::kNumIds:
220       __builtin_unreachable();
221   }
222 }
223 
ToUMAString(MutatorId id)224 inline StatsCollector::MetadataString StatsCollector::ToUMAString(
225     MutatorId id) const {
226   PA_DCHECK(process_name_);
227   const MetadataString process_name = process_name_;
228   switch (id) {
229     case MutatorId::kClear:
230       return "PA.PCScan." + process_name + ".Mutator.Clear";
231     case MutatorId::kScanStack:
232       return "PA.PCScan." + process_name + ".Mutator.ScanStack";
233     case MutatorId::kScan:
234       return "PA.PCScan." + process_name + ".Mutator.Scan";
235     case MutatorId::kOverall:
236       return "PA.PCScan." + process_name + ".Mutator";
237     case MutatorId::kNumIds:
238       __builtin_unreachable();
239   }
240 }
241 
242 #undef FOR_ALL_PCSCAN_MUTATOR_SCOPES
243 #undef FOR_ALL_PCSCAN_SCANNER_SCOPES
244 
245 }  // namespace internal
246 }  // namespace partition_alloc
247 
248 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_STARSCAN_STATS_COLLECTOR_H_
249