1 /*
2  * Copyright (c) 2021 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 
16 #ifndef ECMASCRIPT_MEM_GC_STATS_H
17 #define ECMASCRIPT_MEM_GC_STATS_H
18 
19 #include <chrono>
20 #include <cstring>
21 #include <ctime>
22 
23 #include "ecmascript/common.h"
24 #include "ecmascript/mem/clock_scope.h"
25 #include "ecmascript/mem/mem_common.h"
26 #include "libpandabase/macros.h"
27 
28 namespace panda::ecmascript {
29 class Heap;
30 class SharedHeap;
31 
32 enum class GCType : int {
33     PARTIAL_EDEN_GC,
34     PARTIAL_YOUNG_GC,
35     PARTIAL_OLD_GC,
36     COMPRESS_GC,
37     SHARED_GC,
38     SHARED_FULL_GC,
39     OTHER,
40     START,
41 };
42 
43 enum class RecordData : uint8_t {
44 #define DEFINE_SCOPE(scope) scope,
45     RECORD_DATA(DEFINE_SCOPE)
46 #undef DEFINE_SCOPE
47     NUM_OF_DATA,
48     FIRST_DATA = START_OBJ_SIZE,
49     END_RECORD_OVERWRITE = COLLECT_REGION_SET_SIZE,
50 };
51 
52 enum class SpeedData : uint8_t {
53 #define DEFINE_SCOPE(scope) scope,
54     TRACE_GC_SPEED(DEFINE_SCOPE)
55 #undef DEFINE_SCOPE
56     NUM_OF_SPEED,
57 };
58 
59 enum class RecordDuration : uint8_t {
60 #define DEFINE_SCOPE(scope) scope,
61     RECORD_DURATION(DEFINE_SCOPE)
62 #undef DEFINE_SCOPE
63     NUM_OF_DURATION,
64     FIRST_DATA = SEMI_MIN_PAUSE,
65 };
66 
67 #define TRACE_GC(scope_id, gc_stats)    \
68     [[maybe_unused]] GCStats::Scope sp(scope_id, gc_stats)
69 
70 class GCStats {
71     using Duration = std::chrono::duration<uint64_t, std::nano>;
72 
73 public:
GCStats(const Heap *heap)74     explicit GCStats(const Heap *heap) : heap_(heap) {}
GCStats(const Heap *heap, size_t longPuaseTime)75     GCStats(const Heap *heap, size_t longPuaseTime) : heap_(heap),
76         longPauseTime_(longPuaseTime) {}
77     virtual ~GCStats() = default;
78 
79     virtual void PrintStatisticResult();
80     virtual void PrintGCMemoryStatistic();
81     bool CheckIfLongTimePause();
82     virtual void PrintGCStatistic();
83 
GetGCSpeed(SpeedData data)84     float GetGCSpeed(SpeedData data)
85     {
86         return gcSpeed_[(uint8_t)data];
87     }
88 
SetRecordData(RecordData dataIdx, size_t value)89     void SetRecordData(RecordData dataIdx, size_t value)
90     {
91         recordData_[GetRecordDataIndex(dataIdx)] = value;
92     }
93 
GetRecordData(RecordData dataIdx)94     size_t GetRecordData(RecordData dataIdx)
95     {
96         return recordData_[GetRecordDataIndex(dataIdx)];
97     }
98 
SetGCReason(GCReason reason)99     void SetGCReason(GCReason reason)
100     {
101         reason_ = reason;
102     }
103 
GetGCReason() const104     GCReason GetGCReason() const
105     {
106         return reason_;
107     }
108 
GetGCTypeName()109     const char *GetGCTypeName()
110     {
111         switch (gcType_) {
112             case GCType::PARTIAL_EDEN_GC:
113                 return "HPP EdenGC";
114             case GCType::PARTIAL_YOUNG_GC:
115                 return "HPP YoungGC";
116             case GCType::PARTIAL_OLD_GC:
117                 return "HPP OldGC";
118             case GCType::COMPRESS_GC:
119                 return "CompressGC";
120             case GCType::SHARED_GC:
121                 return "SharedGC";
122             case GCType::SHARED_FULL_GC:
123                 return "SharedCompressGC";
124             default:
125                 return "UnknownType";
126         }
127     }
128 
129     static const char *GCReasonToString(GCReason reason);
130     const char *GCReasonToString();
131 
GetAvgSurvivalRate()132     double GetAvgSurvivalRate()
133     {
134         if (gcType_ == GCType::PARTIAL_EDEN_GC) {
135             size_t commitSize = GetRecordData(RecordData::EDEN_TOTAL_COMMIT);
136             if (commitSize == 0) {
137                 return 0;
138             }
139             double copiedRate = double(GetRecordData(RecordData::EDEN_TOTAL_ALIVE)) / commitSize;
140             double promotedRate = double(GetRecordData(RecordData::EDEN_TOTAL_PROMOTE)) / commitSize;
141             return std::min(copiedRate + promotedRate, 1.0);
142         }
143         double copiedRate = double(GetRecordData(RecordData::YOUNG_TOTAL_ALIVE)) /
144                             GetRecordData(RecordData::YOUNG_TOTAL_COMMIT);
145         double promotedRate = double(GetRecordData(RecordData::YOUNG_TOTAL_PROMOTE)) /
146                               GetRecordData(RecordData::YOUNG_TOTAL_COMMIT);
147         return std::min(copiedRate + promotedRate, 1.0);
148     }
149 
150     virtual void RecordGCSpeed();
151     virtual void RecordStatisticBeforeGC(TriggerGCType gcType, GCReason reason);
152     virtual void RecordStatisticAfterGC();
153 
154     class Scope : public ClockScope {
155     public:
156         enum ScopeId : uint8_t {
157 #define DEFINE_SCOPE(scope) scope,
158             SCOPE_LIST(DEFINE_SCOPE)
159 #undef DEFINE_SCOPE
160             SCOPE_NUM,
161         };
162 
Scope(ScopeId id, GCStats* stats)163         Scope(ScopeId id, GCStats* stats) : id_(id), stats_(stats)
164         {
165             if (id_ == ScopeId::ConcurrentMark) {
166                 stats_->NotifyConcurrentMark();
167             }
168         }
~Scope()169         ~Scope()
170         {
171             float duration = stats_->PrintTimeMilliseconds(stats_->TimeToMicroseconds(GetPauseTime()));
172             stats_->SetScopeId(id_, duration);
173         }
174 
175     private:
176         ScopeId id_;
177         GCStats* stats_;
178     };
179 
GetScopeDuration(int pos) const180     float GetScopeDuration(int pos) const
181     {
182         return scopeDuration_[pos];
183     }
184 
IncreaseTotalDuration(float duration)185     void IncreaseTotalDuration(float duration)
186     {
187         gcDuration_ += duration;
188     }
189 
GetGCCount()190     size_t GetGCCount()
191     {
192         return GetRecordData(RecordData::SEMI_COUNT) + GetRecordData(RecordData::YOUNG_COUNT) +
193             GetRecordData(RecordData::OLD_COUNT) + GetRecordData(RecordData::COMPRESS_COUNT) +
194             GetRecordData(RecordData::SHARED_COUNT);
195     }
196 
GetGCDuration() const197     size_t GetGCDuration() const
198     {
199         return static_cast<size_t>(gcDuration_);
200     }
201 
202     virtual size_t GetAccumulatedAllocateSize();
GetAccumulatedFreeSize() const203     size_t GetAccumulatedFreeSize() const
204     {
205         return accumulatedFreeSize_;
206     }
207 
IncreaseFullGCLongTimeCount()208     void IncreaseFullGCLongTimeCount()
209     {
210         fullGCLongTimeCount_ += 1;
211     }
212 
GetFullGCLongTimeCount() const213     size_t GetFullGCLongTimeCount() const
214     {
215         return fullGCLongTimeCount_;
216     }
217 
218 protected:
219     bool CheckIfNeedPrint(GCType type);
220     void PrintVerboseGCStatistic();
221     void PrintGCDurationStatistic();
222     void PrintGCSummaryStatistic(GCType type = GCType::START);
223     GCType GetGCType(TriggerGCType gcType);
224     void InitializeRecordList();
225     float GetConcurrrentMarkDuration();
226 
GetRecordDurationIndex(RecordDuration durationIdx)227     int GetRecordDurationIndex(RecordDuration durationIdx)
228     {
229         return (int)durationIdx - (int)RecordDuration::FIRST_DATA;
230     }
231 
GetRecordDuration(RecordDuration durationIdx)232     float GetRecordDuration(RecordDuration durationIdx)
233     {
234         return recordDuration_[GetRecordDurationIndex(durationIdx)];
235     }
236 
SetRecordDuration(RecordDuration durationIdx, float value)237     void SetRecordDuration(RecordDuration durationIdx, float value)
238     {
239         recordDuration_[GetRecordDurationIndex(durationIdx)] = value;
240     }
241 
IncreaseRecordDuration(RecordDuration durationIdx, float value)242     void IncreaseRecordDuration(RecordDuration durationIdx, float value)
243     {
244         recordDuration_[GetRecordDurationIndex(durationIdx)] += value;
245     }
246 
GetRecordDataIndex(RecordData dataIdx)247     int GetRecordDataIndex(RecordData dataIdx)
248     {
249         return (int)dataIdx - (int)RecordData::FIRST_DATA;
250     }
251 
IncreaseRecordData(RecordData dataIdx, size_t value = 1)252     void IncreaseRecordData(RecordData dataIdx, size_t value = 1)
253     {
254         recordData_[GetRecordDataIndex(dataIdx)] += value;
255     }
256 
SetScopeId(int pos, float duration)257     void SetScopeId(int pos, float duration)
258     {
259         scopeDuration_[pos] = duration;
260     }
261 
NotifyConcurrentMark()262     void NotifyConcurrentMark()
263     {
264         concurrentMark_ = true;
265     }
266 
IncreaseAccumulatedFreeSize(size_t size)267     void IncreaseAccumulatedFreeSize(size_t size)
268     {
269         accumulatedFreeSize_ += size;
270     }
271 
TimeToMicroseconds(Duration time)272     size_t TimeToMicroseconds(Duration time)
273     {
274         return std::chrono::duration_cast<std::chrono::microseconds>(time).count();
275     }
276 
PrintTimeMilliseconds(uint64_t ms)277     float PrintTimeMilliseconds(uint64_t ms)
278     {
279         return (float)ms / THOUSAND;
280     }
281 
sizeToMB(size_t size)282     float sizeToMB(size_t size)
283     {
284         return (float)size / 1_MB;
285     }
286 
sizeToKB(size_t size)287     float sizeToKB(size_t size)
288     {
289         return (float)size / 1_KB;
290     }
291 
292     const Heap *heap_ {nullptr};
293     float gcDuration_ = 0.0f;
294     size_t longPauseTime_ = 0;
295     size_t fullGCLongTimeCount_ = 0;
296     size_t accumulatedFreeSize_ = 0;
297 
298     static constexpr size_t DEFAULT_UPDATE_REFERENCE_SPEED = 10_MB;
299     static constexpr size_t DEFAULT_OLD_CLEAR_NATIVE_OBJ_SPEED = 1_KB;
300     static constexpr size_t DEFAULT_OLD_EVACUATE_SPACE_SPEED = 600_KB;
301     static constexpr size_t DEFAULT_YOUNG_CLEAR_NATIVE_OBJ_SPEED = 3_KB;
302 
303     GCType gcType_ {GCType::START};
304     GCReason reason_ {GCReason::OTHER};
305     float scopeDuration_[Scope::ScopeId::SCOPE_NUM] {0.0f};
306     size_t recordData_[(uint8_t)RecordData::NUM_OF_DATA] {0};
307     size_t gcSpeed_ [(uint8_t)SpeedData::NUM_OF_SPEED] = {
308         DEFAULT_UPDATE_REFERENCE_SPEED, DEFAULT_OLD_CLEAR_NATIVE_OBJ_SPEED,
309         DEFAULT_OLD_EVACUATE_SPACE_SPEED, DEFAULT_YOUNG_CLEAR_NATIVE_OBJ_SPEED};
310     float recordDuration_[(uint8_t)RecordDuration::NUM_OF_DURATION] {0.0f};
311     bool concurrentMark_ {false};
312 
313     static constexpr uint32_t THOUSAND = 1000;
314 
315     NO_COPY_SEMANTIC(GCStats);
316     NO_MOVE_SEMANTIC(GCStats);
317 };
318 
319 class SharedGCStats : public GCStats {
320 public:
SharedGCStats(const SharedHeap *sHeap, bool enableGCTracer)321     SharedGCStats(const SharedHeap *sHeap, bool enableGCTracer)
322         : GCStats(nullptr), sHeap_(sHeap), enableGCTracer_(enableGCTracer)
323     {
324         SetRecordData(RecordData::SHARED_COUNT, 0);
325     }
326     ~SharedGCStats() = default;
327 
328     void PrintStatisticResult() override;
329     void PrintGCMemoryStatistic() override;
330     void PrintGCStatistic() override;
331 
332     void RecordStatisticBeforeGC(TriggerGCType gcType, GCReason reason) override;
333     void RecordStatisticAfterGC() override;
334     size_t GetAccumulatedAllocateSize() override;
335 private:
336     void PrintSharedGCDuration();
337     void PrintSharedGCSummaryStatistic();
338 
339     const SharedHeap *sHeap_ {nullptr};
340     bool enableGCTracer_ {false};
341 };
342 }  // namespace panda::ecmascript
343 
344 #endif  // ECMASCRIPT_MEM_GC_STATS_H
345