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