1/* 2 * Copyright (c) 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 16#ifndef ECMASCRIPT_PGO_PROFILER_PGO_TRACE_H 17#define ECMASCRIPT_PGO_PROFILER_PGO_TRACE_H 18 19#include <iomanip> 20#include <memory> 21#include <numeric> 22#include <unordered_map> 23 24#include "ecmascript/method.h" 25 26namespace panda::ecmascript::pgo { 27class PGOTrace { 28public: 29 static constexpr int NAME_WIDTH = 40; 30 static constexpr int WIDTH = 20; 31 static constexpr int COLUMN_WIDTH = NAME_WIDTH + WIDTH * 6; 32 static constexpr const char* TITLE_TEXT = " PGO Trace "; 33 static constexpr const float MIN_PRINT_TIME = 10.0; 34 static constexpr const int MIN_PRINT_COUNT = 10; 35 static constexpr const int PRECISION = 3; 36 static constexpr const int TOTAL_COLUMN = 6; 37 static constexpr const int PROFILE_BYTECODE_TIME_COLUMN = 2; 38 39 static std::string Title() 40 { 41 int halfWidth = (COLUMN_WIDTH - std::strlen(TITLE_TEXT)) / 2 + 1; 42 return std::string(halfWidth, '=') + TITLE_TEXT + std::string(halfWidth, '='); 43 } 44 45 static std::string Separator() 46 { 47 return std::string(Title().length(), '='); 48 } 49 50 static std::shared_ptr<PGOTrace> GetInstance() 51 { 52 static auto trace = std::make_shared<PGOTrace>(); 53 return trace; 54 } 55 56 void SetEnable(bool enable) 57 { 58 LOG_ECMA(DEBUG) << "PGO Trace enable: " << enable; 59 enable_ = enable; 60 } 61 62 bool IsEnable() const 63 { 64 return enable_; 65 } 66 67 class MethodData { 68 public: 69 MethodData(JSTaggedValue value, bool hotness) 70 { 71 Method* method = Method::Cast(value); 72 name_ = method->GetMethodName(); 73 id_ = method->GetMethodId(); 74 codesize_ = method->GetCodeSize(); 75 hotness_ = hotness; 76 } 77 78 EntityId GetId() const 79 { 80 return id_; 81 } 82 83 void SetProfileBytecodeTime(float time) 84 { 85 profileBytecodeTime_.push_back(time); 86 } 87 88 void Print() const 89 { 90 float totalTime = std::accumulate(profileBytecodeTime_.begin(), profileBytecodeTime_.end(), 0.0); 91 float count = profileBytecodeTime_.size(); 92#if defined(PANDA_TARGET_OHOS) && !defined(STANDALONE_MODE) 93 if (totalTime < MIN_PRINT_TIME && count < MIN_PRINT_COUNT) { 94 return; 95 } 96#endif 97 if (count <= 0) { 98 return; 99 } 100 float avgProfileBytecodeTime = totalTime / count; 101 LOG_TRACE(INFO) << std::left << std::setw(NAME_WIDTH) << name_ << std::right << std::setw(WIDTH) << id_ 102 << std::setw(WIDTH) << codesize_ << std::setw(WIDTH) << hotness_ << std::setw(WIDTH) 103 << count << std::fixed << std::setprecision(PRECISION) << std::setw(WIDTH) 104 << avgProfileBytecodeTime << std::setw(WIDTH) << totalTime; 105 } 106 107 void SetHotness(bool hotness) 108 { 109 hotness_ = hotness; 110 } 111 112 private: 113 const char* name_; 114 EntityId id_; 115 uint32_t codesize_; 116 std::list<float> profileBytecodeTime_; 117 bool hotness_; 118 }; 119 120 MethodData* GetMethodData(EntityId id) 121 { 122 auto iter = methods.find(id); 123 if (iter == methods.end()) { 124 return nullptr; 125 } 126 return &(iter->second); 127 } 128 129 MethodData* TryGetMethodData(JSTaggedValue value, bool hotness = false) 130 { 131 MethodData method(value, hotness); 132 auto data = GetMethodData(method.GetId()); 133 if (data) { 134 data->SetHotness(hotness); 135 return data; 136 } else { 137 auto res = methods.emplace(method.GetId(), method); 138 return &(res.first->second); 139 } 140 } 141 142 void Print() const 143 { 144 LOG_TRACE(INFO) << "only print methods which (total time > 10 ms || count > 10) on ohos device"; 145 LOG_TRACE(INFO) << Title(); 146 LOG_TRACE(INFO) << std::setw(NAME_WIDTH + WIDTH * (TOTAL_COLUMN - PROFILE_BYTECODE_TIME_COLUMN)) << "" 147 << std::right << std::setw(WIDTH * PROFILE_BYTECODE_TIME_COLUMN) << "Profile Bytecode Time(ms)"; 148 LOG_TRACE(INFO) << std::left << std::setw(NAME_WIDTH) << "Name" << std::right << std::setw(WIDTH) << "Id" 149 << std::setw(WIDTH) << "CodeSize" << std::setw(WIDTH) << "Hotness" << std::setw(WIDTH) 150 << "Count" << std::setw(WIDTH) << "Avg(ms)" << std::setw(WIDTH) << "Total(ms)"; 151 for (auto& [id, method]: methods) { 152 method.Print(); 153 } 154 LOG_TRACE(INFO) << Separator(); 155 LOG_TRACE(INFO) << std::left << std::setw(NAME_WIDTH) << "MergeBeforeSave(ms) " << mergeTime_; 156 LOG_TRACE(INFO) << std::left << std::setw(NAME_WIDTH) << "MergeWithExistAP(ms) " << mergeWithExistProfileTime_; 157 LOG_TRACE(INFO) << std::left << std::setw(NAME_WIDTH) << "SaveAndRename(ms) " << saveTime_; 158 LOG_TRACE(INFO) << Separator(); 159 } 160 161 void SetMergeTime(float time) 162 { 163 mergeTime_ = time; 164 } 165 166 void SetSaveTime(float time) 167 { 168 saveTime_ = time; 169 } 170 171 void SetMergeWithExistProfileTime(float time) 172 { 173 mergeWithExistProfileTime_ = time; 174 } 175 176private: 177 std::unordered_map<EntityId, MethodData> methods; 178 float mergeTime_; 179 float mergeWithExistProfileTime_; 180 float saveTime_; 181 bool enable_ {false}; 182}; 183} // namespace panda::ecmascript::pgo 184#endif // ECMASCRIPT_PGO_PROFILER_PGO_TRACE_H