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 26 namespace panda::ecmascript::pgo { 27 class PGOTrace { 28 public: 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 Title()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 Separator()45 static std::string Separator() 46 { 47 return std::string(Title().length(), '='); 48 } 49 GetInstance()50 static std::shared_ptr<PGOTrace> GetInstance() 51 { 52 static auto trace = std::make_shared<PGOTrace>(); 53 return trace; 54 } 55 SetEnable(bool enable)56 void SetEnable(bool enable) 57 { 58 LOG_PGO(DEBUG) << "pgo trace is " << (enable ? "enabled" : "disabled"); 59 enable_ = enable; 60 } 61 IsEnable()62 bool IsEnable() const 63 { 64 return enable_; 65 } 66 67 class MethodData { 68 public: MethodData(const JSThread * thread,JSTaggedValue value,bool hotness)69 MethodData(const JSThread *thread, JSTaggedValue value, bool hotness) 70 { 71 Method* method = Method::Cast(value); 72 name_ = method->GetMethodName(thread); 73 id_ = method->GetMethodId(); 74 codesize_ = method->GetCodeSize(thread); 75 hotness_ = hotness; 76 } 77 GetId()78 EntityId GetId() const 79 { 80 return id_; 81 } 82 SetProfileBytecodeTime(float time)83 void SetProfileBytecodeTime(float time) 84 { 85 profileBytecodeTime_.push_back(time); 86 } 87 Print()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 SetHotness(bool hotness)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 GetMethodData(EntityId id)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(const JSThread *thread, JSTaggedValue value, bool hotness = false) 130 { 131 MethodData method(thread, 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 Print()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) << "MergeWithExistAP(ms) " << mergeWithExistProfileTime_; 156 LOG_TRACE(INFO) << std::left << std::setw(NAME_WIDTH) << "SaveAndRename(ms) " << saveTime_; 157 LOG_TRACE(INFO) << Separator(); 158 } 159 SetSaveTime(float time)160 void SetSaveTime(float time) 161 { 162 saveTime_ = time; 163 } 164 SetMergeWithExistProfileTime(float time)165 void SetMergeWithExistProfileTime(float time) 166 { 167 mergeWithExistProfileTime_ = time; 168 } 169 170 private: 171 std::unordered_map<EntityId, MethodData> methods; 172 float mergeWithExistProfileTime_; 173 float saveTime_; 174 bool enable_ {false}; 175 }; 176 } // namespace panda::ecmascript::pgo 177 #endif // ECMASCRIPT_PGO_PROFILER_PGO_TRACE_H