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