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
28namespace panda::ecmascript {
29class Heap;
30class SharedHeap;
31
32enum 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
43enum 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
52enum 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
59enum 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
70class GCStats {
71    using Duration = std::chrono::duration<uint64_t, std::nano>;
72
73public:
74    explicit GCStats(const Heap *heap) : heap_(heap) {}
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
84    float GetGCSpeed(SpeedData data)
85    {
86        return gcSpeed_[(uint8_t)data];
87    }
88
89    void SetRecordData(RecordData dataIdx, size_t value)
90    {
91        recordData_[GetRecordDataIndex(dataIdx)] = value;
92    }
93
94    size_t GetRecordData(RecordData dataIdx)
95    {
96        return recordData_[GetRecordDataIndex(dataIdx)];
97    }
98
99    void SetGCReason(GCReason reason)
100    {
101        reason_ = reason;
102    }
103
104    GCReason GetGCReason() const
105    {
106        return reason_;
107    }
108
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
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
163        Scope(ScopeId id, GCStats* stats) : id_(id), stats_(stats)
164        {
165            if (id_ == ScopeId::ConcurrentMark) {
166                stats_->NotifyConcurrentMark();
167            }
168        }
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
180    float GetScopeDuration(int pos) const
181    {
182        return scopeDuration_[pos];
183    }
184
185    void IncreaseTotalDuration(float duration)
186    {
187        gcDuration_ += duration;
188    }
189
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
197    size_t GetGCDuration() const
198    {
199        return static_cast<size_t>(gcDuration_);
200    }
201
202    virtual size_t GetAccumulatedAllocateSize();
203    size_t GetAccumulatedFreeSize() const
204    {
205        return accumulatedFreeSize_;
206    }
207
208    void IncreaseFullGCLongTimeCount()
209    {
210        fullGCLongTimeCount_ += 1;
211    }
212
213    size_t GetFullGCLongTimeCount() const
214    {
215        return fullGCLongTimeCount_;
216    }
217
218protected:
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
227    int GetRecordDurationIndex(RecordDuration durationIdx)
228    {
229        return (int)durationIdx - (int)RecordDuration::FIRST_DATA;
230    }
231
232    float GetRecordDuration(RecordDuration durationIdx)
233    {
234        return recordDuration_[GetRecordDurationIndex(durationIdx)];
235    }
236
237    void SetRecordDuration(RecordDuration durationIdx, float value)
238    {
239        recordDuration_[GetRecordDurationIndex(durationIdx)] = value;
240    }
241
242    void IncreaseRecordDuration(RecordDuration durationIdx, float value)
243    {
244        recordDuration_[GetRecordDurationIndex(durationIdx)] += value;
245    }
246
247    int GetRecordDataIndex(RecordData dataIdx)
248    {
249        return (int)dataIdx - (int)RecordData::FIRST_DATA;
250    }
251
252    void IncreaseRecordData(RecordData dataIdx, size_t value = 1)
253    {
254        recordData_[GetRecordDataIndex(dataIdx)] += value;
255    }
256
257    void SetScopeId(int pos, float duration)
258    {
259        scopeDuration_[pos] = duration;
260    }
261
262    void NotifyConcurrentMark()
263    {
264        concurrentMark_ = true;
265    }
266
267    void IncreaseAccumulatedFreeSize(size_t size)
268    {
269        accumulatedFreeSize_ += size;
270    }
271
272    size_t TimeToMicroseconds(Duration time)
273    {
274        return std::chrono::duration_cast<std::chrono::microseconds>(time).count();
275    }
276
277    float PrintTimeMilliseconds(uint64_t ms)
278    {
279        return (float)ms / THOUSAND;
280    }
281
282    float sizeToMB(size_t size)
283    {
284        return (float)size / 1_MB;
285    }
286
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
319class SharedGCStats : public GCStats {
320public:
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;
335private:
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