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_DFX_PGO_PROFILER_MANAGER_H 17 #define ECMASCRIPT_DFX_PGO_PROFILER_MANAGER_H 18 19 #include <algorithm> 20 #include <cstring> 21 #include <memory> 22 23 #include "ecmascript/base/file_header.h" 24 #include "ecmascript/ecma_macros.h" 25 #include "ecmascript/ecma_vm.h" 26 #include "ecmascript/log_wrapper.h" 27 #include "ecmascript/mem/chunk_containers.h" 28 #include "ecmascript/mem/native_area_allocator.h" 29 #include "ecmascript/taskpool/task.h" 30 31 namespace panda::ecmascript { 32 enum class SampleMode : uint8_t { 33 HOTNESS_MODE, 34 CALL_MODE, 35 }; 36 37 /* 38 * Support statistics of JS Function call heat. Save the method ID whose calls are less than MIN_COUNT. 39 * 40 * The saving format is as follows: 41 * "recordName1:[methodId/count/mode/name,methodId/count/mode/name......]" 42 * "recordName2:[methodId/count/mode/name,methodId/count/mode/name,methodId/count/mode/name......]" 43 * "......" 44 * */ 45 class MethodProfilerInfo { 46 public: 47 static constexpr size_t ALIGN_SIZE = 4; 48 MethodProfilerInfo(EntityId id,uint32_t count,SampleMode mode,uint16_t length)49 MethodProfilerInfo(EntityId id, uint32_t count, SampleMode mode, uint16_t length) 50 : id_(id), count_(count), mode_(mode), methodLength_(length) {} 51 Size(uint32_t length)52 static int32_t Size(uint32_t length) 53 { 54 return sizeof(MethodProfilerInfo) + AlignUp(length, ALIGN_SIZE); 55 } 56 Size()57 int32_t Size() 58 { 59 return sizeof(MethodProfilerInfo) + AlignUp(methodLength_, ALIGN_SIZE); 60 } 61 IncreaseCount()62 void IncreaseCount() 63 { 64 count_++; 65 } 66 ClearCount()67 void ClearCount() 68 { 69 count_ = 0; 70 } 71 Merge(const MethodProfilerInfo * info)72 void Merge(const MethodProfilerInfo *info) 73 { 74 count_ += info->GetCount(); 75 methodLength_ = info->GetMethodLength(); 76 SetSampleMode(info->GetSampleMode()); 77 SetMethodName(info->GetMethodName(), info->GetMethodLength()); 78 } 79 GetMethodId()80 EntityId GetMethodId() const 81 { 82 return id_; 83 } 84 GetCount()85 uint32_t GetCount() const 86 { 87 return count_; 88 } 89 GetMethodLength()90 uint16_t GetMethodLength() const 91 { 92 return methodLength_; 93 } 94 SetMethodName(const char * methodName,size_t len)95 void SetMethodName(const char *methodName, size_t len) 96 { 97 if (memcpy_s(&methodName_, methodLength_, methodName, len) != EOK) { 98 LOG_ECMA(ERROR) << "SetMethodName memcpy_s failed" << methodName << ", len = " << len; 99 } 100 *(&methodName_ + len) = '\0'; 101 } 102 GetMethodName()103 const char *GetMethodName() const 104 { 105 return &methodName_; 106 } 107 SetSampleMode(SampleMode mode)108 void SetSampleMode(SampleMode mode) 109 { 110 if (mode_ == SampleMode::HOTNESS_MODE) { 111 return; 112 } 113 mode_ = mode; 114 } 115 GetSampleMode()116 SampleMode GetSampleMode() const 117 { 118 return mode_; 119 } 120 121 NO_COPY_SEMANTIC(MethodProfilerInfo); 122 NO_MOVE_SEMANTIC(MethodProfilerInfo); 123 124 private: 125 EntityId id_; 126 uint32_t count_ {0}; 127 SampleMode mode_ {SampleMode::CALL_MODE}; 128 uint16_t methodLength_ {0}; 129 char methodName_ {'\0'}; 130 }; 131 132 class PGOProfilerHeader : public base::FileHeader { 133 public: 134 static constexpr std::array<uint8_t, VERSION_SIZE> LAST_VERSION = {0, 0, 0, 1}; 135 PGOProfilerHeader()136 PGOProfilerHeader() : base::FileHeader(LAST_VERSION) {} 137 Verify()138 bool Verify() 139 { 140 return VerifyInner(LAST_VERSION); 141 } 142 }; 143 144 class PandaFileProfilerInfo { 145 public: 146 PandaFileProfilerInfo() = default; PandaFileProfilerInfo(uint32_t checksum)147 PandaFileProfilerInfo(uint32_t checksum) : checksum_(checksum) {} 148 GetChecksum()149 uint32_t GetChecksum() 150 { 151 return checksum_; 152 } 153 154 private: 155 uint32_t checksum_; 156 }; 157 158 class PGOProfiler { 159 public: 160 NO_COPY_SEMANTIC(PGOProfiler); 161 NO_MOVE_SEMANTIC(PGOProfiler); 162 163 void Sample(JSTaggedType value, SampleMode mode = SampleMode::CALL_MODE); 164 private: PGOProfiler(EcmaVM * vm,bool isEnable)165 PGOProfiler(EcmaVM *vm, bool isEnable) 166 : isEnable_(isEnable), chunk_(vm->GetNativeAreaAllocator()), profilerMap_(&chunk_) {}; 167 virtual ~PGOProfiler(); Reset(bool isEnable)168 void Reset(bool isEnable) 169 { 170 isEnable_ = isEnable; 171 profilerMap_.clear(); 172 methodCount_ = 0; 173 } 174 175 static constexpr uint32_t MERGED_EVERY_COUNT = 10; 176 bool isEnable_ {false}; 177 Chunk chunk_; 178 ChunkUnorderedMap<CString, ChunkUnorderedMap<EntityId, MethodProfilerInfo *> *> profilerMap_; 179 uint32_t methodCount_ {0}; 180 friend class PGOProfilerManager; 181 }; 182 183 class PGOProfilerManager { 184 public: GetInstance()185 static PGOProfilerManager *GetInstance() 186 { 187 static PGOProfilerManager instance; 188 return &instance; 189 } 190 191 PGOProfilerManager() = default; 192 ~PGOProfilerManager() = default; 193 194 NO_COPY_SEMANTIC(PGOProfilerManager); 195 NO_MOVE_SEMANTIC(PGOProfilerManager); 196 197 void Initialize(uint32_t hotnessThreshold, const std::string &outDir); 198 bool InitializeData(); 199 200 void Destroy(); 201 202 // Factory Build(EcmaVM * vm,bool isEnable)203 PGOProfiler *Build(EcmaVM *vm, bool isEnable) 204 { 205 if (isEnable) { 206 isEnable = InitializeData(); 207 } 208 return new PGOProfiler(vm, isEnable); 209 } 210 Reset(PGOProfiler * profiler,bool isEnable)211 void Reset(PGOProfiler *profiler, bool isEnable) 212 { 213 if (profiler) { 214 profiler->Reset(isEnable); 215 } 216 if (isEnable) { 217 InitializeData(); 218 } 219 } 220 Destroy(PGOProfiler * profiler)221 void Destroy(PGOProfiler *profiler) 222 { 223 if (profiler != nullptr) { 224 Merge(profiler); 225 delete profiler; 226 } 227 } 228 229 void SamplePandaFileInfo(uint32_t checksum); 230 void Merge(PGOProfiler *profile); 231 void TerminateSaveTask(); 232 void PostSaveTask(); 233 234 private: 235 class SaveTask : public Task { 236 public: SaveTask(int32_t id)237 SaveTask(int32_t id) : Task(id) {}; 238 virtual ~SaveTask() = default; 239 Run(uint32_t threadIndex)240 bool Run([[maybe_unused]] uint32_t threadIndex) override 241 { 242 PGOProfilerManager::GetInstance()->StartSaveTask(this); 243 return true; 244 } 245 GetTaskType()246 TaskType GetTaskType() override 247 { 248 return TaskType::PGO_SAVE_TASK; 249 } 250 251 NO_COPY_SEMANTIC(SaveTask); 252 NO_MOVE_SEMANTIC(SaveTask); 253 }; 254 255 void StartSaveTask(SaveTask *task); 256 void SaveProfiler(SaveTask *task = nullptr); 257 void ProcessProfileHeader(std::ofstream &fileStream); 258 void ProcessPandaFileInfo(std::ofstream &fileStream); 259 void ProcessProfile(std::ofstream &fileStream, SaveTask *task); 260 261 bool isInitialized_ {false}; 262 uint32_t hotnessThreshold_ {2}; 263 std::string outDir_; 264 std::string realOutPath_; 265 std::unique_ptr<NativeAreaAllocator> nativeAreaAllocator_; 266 std::unique_ptr<Chunk> chunk_; 267 PGOProfilerHeader header_; 268 ChunkUnorderedMap<CString, ChunkUnorderedMap<EntityId, MethodProfilerInfo *> *> *globalProfilerMap_; 269 ChunkVector<PandaFileProfilerInfo *> *pandaFileProfilerInfos_; 270 os::memory::Mutex mutex_; 271 }; 272 273 } // namespace panda::ecmascript 274 #endif // ECMASCRIPT_DFX_PGO_PROFILER_MANAGER_H 275