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