1 /**
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #ifndef PANDA_RUNTIME_MEM_GC_GC_STATS_H
16 #define PANDA_RUNTIME_MEM_GC_GC_STATS_H
17
18 #include "libpandabase/os/mutex.h"
19 #include "runtime/include/histogram-inl.h"
20 #include "runtime/include/mem/panda_containers.h"
21 #include "runtime/include/mem/panda_string.h"
22 #include "runtime/include/time_utils.h"
23 #include "runtime/mem/gc/gc_types.h"
24
25 #include <algorithm>
26 #include <atomic>
27 #include <ratio>
28
29 namespace ark::mem {
30
31 namespace test {
32 class MemStatsGenGCTest;
33 } // namespace test
34
35 class GCStats;
36 class HeapManager;
37
38 enum class ObjectTypeStats : size_t {
39 YOUNG_FREED_OBJECTS = 0,
40 MOVED_OBJECTS,
41 ALL_FREED_OBJECTS,
42
43 OBJECT_TYPE_STATS_LAST
44 };
45
ToIndex(ObjectTypeStats type)46 constexpr size_t ToIndex(ObjectTypeStats type)
47 {
48 return static_cast<size_t>(type);
49 }
50
51 constexpr size_t OBJECT_TYPE_STATS_SIZE = static_cast<size_t>(ObjectTypeStats::OBJECT_TYPE_STATS_LAST);
52
53 enum class MemoryTypeStats : size_t {
54 YOUNG_FREED_BYTES = 0,
55 MOVED_BYTES,
56 ALL_FREED_BYTES,
57
58 MEMORY_TYPE_STATS_LAST
59 };
60
ToIndex(MemoryTypeStats type)61 constexpr size_t ToIndex(MemoryTypeStats type)
62 {
63 return static_cast<size_t>(type);
64 }
65
66 constexpr size_t MEMORY_TYPE_STATS_SIZE = static_cast<size_t>(MemoryTypeStats::MEMORY_TYPE_STATS_LAST);
67
68 enum class TimeTypeStats : size_t {
69 YOUNG_PAUSED_TIME = 0,
70 YOUNG_TOTAL_TIME,
71 ALL_PAUSED_TIME,
72 ALL_TOTAL_TIME,
73
74 TIME_TYPE_STATS_LAST
75 };
76
ToIndex(TimeTypeStats type)77 constexpr size_t ToIndex(TimeTypeStats type)
78 {
79 return static_cast<size_t>(type);
80 }
81
82 constexpr size_t TIME_TYPE_STATS_SIZE = static_cast<size_t>(TimeTypeStats::TIME_TYPE_STATS_LAST);
83
84 enum class PauseTypeStats : size_t {
85 COMMON_PAUSE = 0,
86 INITIAL_MARK_PAUSE,
87 REMARK_PAUSE,
88
89 PAUSE_TYPE_STATS_LAST
90 };
91
ToIndex(PauseTypeStats type)92 constexpr size_t ToIndex(PauseTypeStats type)
93 {
94 return static_cast<size_t>(type);
95 }
96
ToPauseTypeStats(size_t type)97 constexpr PauseTypeStats ToPauseTypeStats(size_t type)
98 {
99 return static_cast<PauseTypeStats>(type);
100 }
101
ToString(PauseTypeStats type)102 constexpr std::string_view ToString(PauseTypeStats type)
103 {
104 switch (type) {
105 case PauseTypeStats::COMMON_PAUSE:
106 return "COMMON_PAUSE";
107 case PauseTypeStats::INITIAL_MARK_PAUSE:
108 return "INITIAL_MARK_PAUSE";
109 case PauseTypeStats::REMARK_PAUSE:
110 return "REMARK_PAUSE";
111 default:
112 return "UnknownPauseType";
113 }
114 }
115
116 constexpr size_t PAUSE_TYPE_STATS_SIZE = static_cast<size_t>(PauseTypeStats::PAUSE_TYPE_STATS_LAST);
117
118 // scoped specific for GC Stats
119 class GCInstanceStats {
120 public:
GCInstanceStats()121 GCInstanceStats()
122 {
123 std::fill(begin(objectsStats_), end(objectsStats_),
124 SimpleHistogram<uint64_t>(helpers::ValueType::VALUE_TYPE_OBJECT));
125 std::fill(begin(memoryStats_), end(memoryStats_),
126 SimpleHistogram<uint64_t>(helpers::ValueType::VALUE_TYPE_MEMORY));
127 std::fill(begin(timeStats_), end(timeStats_), SimpleHistogram<uint64_t>(helpers::ValueType::VALUE_TYPE_TIME));
128 }
129
AddObjectsValue(uint64_t value,ObjectTypeStats memoryType)130 void AddObjectsValue(uint64_t value, ObjectTypeStats memoryType)
131 {
132 auto index = static_cast<size_t>(memoryType);
133 objectsStats_[index].AddValue(value);
134 }
135
AddMemoryValue(uint64_t value,MemoryTypeStats memoryType)136 void AddMemoryValue(uint64_t value, MemoryTypeStats memoryType)
137 {
138 auto index = static_cast<size_t>(memoryType);
139 memoryStats_[index].AddValue(value);
140 }
141
AddTimeValue(uint64_t value,TimeTypeStats timeType)142 void AddTimeValue(uint64_t value, TimeTypeStats timeType)
143 {
144 auto index = static_cast<size_t>(timeType);
145 timeStats_[index].AddValue(value);
146 }
147
AddReclaimRatioValue(double value)148 void AddReclaimRatioValue(double value)
149 {
150 reclaimBytes_.AddValue(value);
151 }
152
AddCopiedRatioValue(double value)153 void AddCopiedRatioValue(double value)
154 {
155 copiedBytes_.AddValue(value);
156 }
157
158 PandaString GetDump(GCType gcType);
159
160 virtual ~GCInstanceStats() = default;
161
162 NO_COPY_SEMANTIC(GCInstanceStats);
163 NO_MOVE_SEMANTIC(GCInstanceStats);
164
165 private:
166 PandaString GetYoungSpaceDump(GCType gcType);
167 PandaString GetAllSpacesDump(GCType gcType);
168 std::array<SimpleHistogram<uint64_t>, OBJECT_TYPE_STATS_SIZE> objectsStats_;
169 std::array<SimpleHistogram<uint64_t>, MEMORY_TYPE_STATS_SIZE> memoryStats_;
170 std::array<SimpleHistogram<uint64_t>, TIME_TYPE_STATS_SIZE> timeStats_;
171 SimpleHistogram<double> reclaimBytes_;
172 SimpleHistogram<double> copiedBytes_;
173 };
174
175 // scoped all field GCStats except pause_
176 class GCScopedStats {
177 public:
178 explicit GCScopedStats(GCStats *stats, GCInstanceStats *instanceStats = nullptr);
179
180 NO_COPY_SEMANTIC(GCScopedStats);
181 NO_MOVE_SEMANTIC(GCScopedStats);
182
183 ~GCScopedStats();
184
185 private:
186 uint64_t startTime_;
187 GCInstanceStats *instanceStats_;
188 GCStats *stats_;
189 };
190
191 // scoped field GCStats while GC in pause
192 class GCScopedPauseStats {
193 public:
194 explicit GCScopedPauseStats(GCStats *stats, GCInstanceStats *instanceStats = nullptr,
195 PauseTypeStats pauseType = PauseTypeStats::COMMON_PAUSE);
196
197 NO_COPY_SEMANTIC(GCScopedPauseStats);
198 NO_MOVE_SEMANTIC(GCScopedPauseStats);
199
200 ~GCScopedPauseStats();
201
202 private:
203 uint64_t startTime_;
204 GCInstanceStats *instanceStats_;
205 GCStats *stats_;
206 PauseTypeStats pauseType_;
207 };
208
209 class GCStats {
210 public:
211 explicit GCStats(MemStatsType *memStats, GCType gcTypeFromRuntime, InternalAllocatorPtr allocator);
212 ~GCStats();
213
214 NO_COPY_SEMANTIC(GCStats);
215 NO_MOVE_SEMANTIC(GCStats);
216
217 PandaString GetStatistics();
218
219 PandaString GetFinalStatistics(HeapManager *heapManager);
220
221 PandaString GetPhasePauseStat(PauseTypeStats pauseType);
222
223 uint64_t GetPhasePause(PauseTypeStats pauseType);
224 void ResetLastPause();
225
GetObjectsFreedBytes()226 size_t GetObjectsFreedBytes()
227 {
228 #ifdef PANDA_TARGET_64
229 static_assert(sizeof(objectsFreedBytes_) == sizeof(std::atomic_uint64_t));
230 // Atomic with seq_cst order reason: data race with objects_freed_bytes_ with requirement for sequentially
231 // consistent order where threads observe all modifications in the same order
232 return reinterpret_cast<std::atomic_uint64_t *>(&objectsFreedBytes_)->load(std::memory_order_seq_cst);
233 #endif
234 #ifdef PANDA_TARGET_32
235 static_assert(sizeof(objectsFreedBytes_) == sizeof(std::atomic_uint32_t));
236 // Atomic with seq_cst order reason: data race with objects_freed_bytes_ with requirement for sequentially
237 // consistent order where threads observe all modifications in the same order
238 return reinterpret_cast<std::atomic_uint32_t *>(&objectsFreedBytes_)->load(std::memory_order_seq_cst);
239 #endif
240 UNREACHABLE();
241 }
242
GetObjectsFreedCount()243 uint64_t GetObjectsFreedCount()
244 {
245 return objectsFreed_;
246 }
247
GetLargeObjectsFreedBytes()248 uint64_t GetLargeObjectsFreedBytes()
249 {
250 return largeObjectsFreedBytes_;
251 }
252
GetLargeObjectsFreedCount()253 uint64_t GetLargeObjectsFreedCount()
254 {
255 return largeObjectsFreed_;
256 }
257
258 void StartMutatorLock();
259 void StopMutatorLock();
260
261 private:
262 // For convert from nano to 10 seconds
263 using PERIOD = std::deca;
264 GCType gcType_ {GCType::INVALID_GC};
265 size_t objectsFreed_ {0};
266 size_t objectsFreedBytes_ {0};
267 size_t largeObjectsFreed_ {0};
268 size_t largeObjectsFreedBytes_ {0};
269 uint64_t startTime_ {0};
270 // CC-OFFNXT(G.FMT.03) project code style
GUARDED_BY(mutatorStatsLock_)271 size_t countMutator_ GUARDED_BY(mutatorStatsLock_) {0};
272 // CC-OFFNXT(G.FMT.03) project code style
GUARDED_BY(mutatorStatsLock_)273 uint64_t mutatorStartTime_ GUARDED_BY(mutatorStatsLock_) {0};
274
275 uint64_t lastDuration_ {0};
276 uint64_t totalDuration_ {0};
277 uint64_t totalPause_ {0};
278 // CC-OFFNXT(G.FMT.03) project code style
GUARDED_BY(mutatorStatsLock_)279 uint64_t totalMutatorPause_ GUARDED_BY(mutatorStatsLock_) {0};
280
281 uint64_t lastStartDuration_ {0};
282 // GC in the last PERIOD
283 uint64_t countGcPeriod_ {0};
284 // GC number of times every PERIOD
285 PandaVector<uint64_t> *allNumberDurations_ {nullptr};
286
287 std::array<uint64_t, PAUSE_TYPE_STATS_SIZE> lastPause_ {};
288
289 os::memory::Mutex mutatorStatsLock_;
290 MemStatsType *memStats_;
291
292 void StartCollectStats();
293 void StopCollectStats(GCInstanceStats *instanceStats);
294
295 void AddPause(uint64_t pause, GCInstanceStats *instanceStats, PauseTypeStats pauseType);
296
297 void RecordDuration(uint64_t duration, GCInstanceStats *instanceStats);
298
299 uint64_t ConvertTimeToPeriod(uint64_t timeInNanos, bool ceil = false);
300
301 InternalAllocatorPtr allocator_ {nullptr};
302
303 #ifndef NDEBUG
304 PauseTypeStats prevPauseType_ {PauseTypeStats::COMMON_PAUSE};
305 #endif
306
307 friend GCScopedPauseStats;
308 friend GCScopedStats;
309 friend test::MemStatsGenGCTest;
310 };
311
312 } // namespace ark::mem
313
314 #endif // PANDA_RUNTIME_MEM_GC_GC_STATS_H
315