1 /* 2 * Copyright (c) 2023 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_INFO_H 17 #define ECMASCRIPT_PGO_PROFILER_INFO_H 18 19 #include <memory> 20 #include <sstream> 21 #include <string.h> 22 23 #include "ecmascript/base/file_header.h" 24 #include "ecmascript/jspandafile/method_literal.h" 25 #include "ecmascript/mem/c_containers.h" 26 #include "ecmascript/mem/native_area_allocator.h" 27 28 namespace panda::ecmascript { 29 class SaveTask; 30 31 enum class SampleMode : uint8_t { 32 HOTNESS_MODE, 33 CALL_MODE, 34 }; 35 36 struct SectionInfo { 37 uint32_t offset_ {0}; 38 // reserve 39 uint32_t size_ {0}; 40 uint32_t number_ {0}; 41 }; 42 static constexpr size_t ALIGN_SIZE = 4; 43 44 /** 45 * |----PGOProfilerHeader 46 * |--------MAGIC 47 * |--------VERSION 48 * |--------SECTION_NUMBER 49 * |--------PANDA_FILE_INFO_SECTION_INFO 50 * |------------offset 51 * |------------size (reserve) 52 * |------------number 53 * |--------RECORD_INFO_SECTION_INFO 54 * |------------offset 55 * |------------size (reserve) 56 * |------------number 57 * |----PGOPandaFileInfos 58 * |--------SIZE 59 * |--------CHECK_SUM 60 * |--------... 61 * |----PGORecordDetailInfos 62 * |--------PGOMethodInfoMap 63 * |------------PGOMethodInfo 64 * |----------------size 65 * |----------------id 66 * |----------------count 67 * |----------------mode 68 * |----------------methodName 69 * |----------------... 70 * |----PGORecordSimpleInfos 71 * |--------PGOMethodIdSet 72 * |------------id 73 * |------------... 74 */ 75 class PGOProfilerHeader : public base::FileHeader { 76 public: 77 static constexpr VersionType LAST_VERSION = {0, 0, 0, 1}; 78 static constexpr size_t SECTION_SIZE = 2; 79 static constexpr size_t PANDA_FILE_SECTION_INDEX = 0; 80 static constexpr size_t RECORD_INFO_SECTION_INDEX = 1; 81 PGOProfilerHeader()82 PGOProfilerHeader() : base::FileHeader(LAST_VERSION), sectionNumber_(SECTION_SIZE) 83 { 84 GetPandaInfoSection()->offset_ = Size(); 85 } 86 LastSize()87 static size_t LastSize() 88 { 89 return sizeof(PGOProfilerHeader) + (SECTION_SIZE - 1) * sizeof(SectionInfo); 90 } 91 Size()92 size_t Size() const 93 { 94 return sizeof(PGOProfilerHeader) + (sectionNumber_ - 1) * sizeof(SectionInfo); 95 } 96 Verify()97 bool Verify() const 98 { 99 return VerifyInner("apPath file", LAST_VERSION); 100 } 101 Build(PGOProfilerHeader ** header,size_t size)102 static void Build(PGOProfilerHeader **header, size_t size) 103 { 104 *header = reinterpret_cast<PGOProfilerHeader *>(malloc(size)); 105 new (*header) PGOProfilerHeader(); 106 } 107 Destroy(PGOProfilerHeader ** header)108 static void Destroy(PGOProfilerHeader **header) 109 { 110 if (*header != nullptr) { 111 free(*header); 112 *header = nullptr; 113 } 114 } 115 116 // Copy Header. 117 static bool ParseFromBinary(void *buffer, PGOProfilerHeader **header); 118 void ProcessToBinary(std::ofstream &fileStream) const; 119 120 bool ParseFromText(std::ifstream &stream); 121 bool ProcessToText(std::ofstream &stream) const; 122 GetPandaInfoSection()123 SectionInfo *GetPandaInfoSection() const 124 { 125 return GetSectionInfo(PANDA_FILE_SECTION_INDEX); 126 } 127 GetRecordInfoSection()128 SectionInfo *GetRecordInfoSection() const 129 { 130 return GetSectionInfo(RECORD_INFO_SECTION_INDEX); 131 } 132 133 NO_COPY_SEMANTIC(PGOProfilerHeader); 134 NO_MOVE_SEMANTIC(PGOProfilerHeader); 135 136 private: GetSectionInfo(size_t index)137 SectionInfo *GetSectionInfo(size_t index) const 138 { 139 if (index >= SECTION_SIZE) { 140 return nullptr; 141 } 142 return const_cast<SectionInfo *>(§ionInfos_) + index; 143 } 144 145 uint32_t sectionNumber_ {SECTION_SIZE}; 146 SectionInfo sectionInfos_; 147 }; 148 149 class PGOPandaFileInfos { 150 public: Sample(uint32_t checksum)151 void Sample(uint32_t checksum) 152 { 153 pandaFileInfos_.insert(checksum); 154 } 155 Clear()156 void Clear() 157 { 158 pandaFileInfos_.clear(); 159 } 160 161 void ParseFromBinary(void *buffer, SectionInfo *const info); 162 void ProcessToBinary(std::ofstream &fileStream, SectionInfo *info) const; 163 164 void ProcessToText(std::ofstream &stream) const; 165 bool ParseFromText(std::ifstream &stream); 166 167 bool CheckSum(uint32_t checksum) const; 168 169 private: 170 class PandaFileInfo { 171 public: 172 PandaFileInfo() = default; PandaFileInfo(uint32_t checksum)173 PandaFileInfo(uint32_t checksum) : size_(LastSize()), checksum_(checksum) {} 174 LastSize()175 static size_t LastSize() 176 { 177 return sizeof(PandaFileInfo); 178 } 179 Size()180 size_t Size() 181 { 182 return static_cast<size_t>(size_); 183 } 184 185 bool operator<(const PandaFileInfo &right) const 186 { 187 return checksum_ < right.checksum_; 188 } 189 GetChecksum()190 uint32_t GetChecksum() const 191 { 192 return checksum_; 193 } 194 private: 195 // Support extended fields 196 uint32_t size_; 197 uint32_t checksum_; 198 }; 199 200 std::set<PandaFileInfo> pandaFileInfos_; 201 }; 202 203 class PGOMethodInfo { 204 public: 205 static constexpr int METHOD_INFO_COUNT = 4; 206 static constexpr int METHOD_ID_INDEX = 0; 207 static constexpr int METHOD_COUNT_INDEX = 1; 208 static constexpr int METHOD_MODE_INDEX = 2; 209 static constexpr int METHOD_NAME_INDEX = 3; 210 PGOMethodInfo(EntityId id)211 PGOMethodInfo(EntityId id) : id_(id) {} 212 PGOMethodInfo(EntityId id,uint32_t count,SampleMode mode,const char * methodName)213 PGOMethodInfo(EntityId id, uint32_t count, SampleMode mode, const char *methodName) 214 : id_(id), count_(count), mode_(mode) 215 { 216 size_t len = strlen(methodName); 217 size_ = static_cast<uint32_t>(Size(len)); 218 if (len > 0 && memcpy_s(&methodName_, len, methodName, len) != EOK) { 219 LOG_ECMA(ERROR) << "SetMethodName memcpy_s failed" << methodName << ", len = " << len; 220 UNREACHABLE(); 221 } 222 *(&methodName_ + len) = '\0'; 223 } 224 Size(uint32_t length)225 static int32_t Size(uint32_t length) 226 { 227 return sizeof(PGOMethodInfo) + AlignUp(length, ALIGN_SIZE); 228 } 229 Size()230 int32_t Size() const 231 { 232 return size_; 233 } 234 GetSampleMode(std::string content,SampleMode & mode)235 static bool GetSampleMode(std::string content, SampleMode &mode) 236 { 237 if (content == "HOTNESS_MODE") { 238 mode = SampleMode::HOTNESS_MODE; 239 } else if (content == "CALL_MODE") { 240 mode = SampleMode::CALL_MODE; 241 } else { 242 return false; 243 } 244 return true; 245 } 246 IncreaseCount()247 void IncreaseCount() 248 { 249 count_++; 250 } 251 ClearCount()252 void ClearCount() 253 { 254 count_ = 0; 255 } 256 Merge(const PGOMethodInfo * info)257 void Merge(const PGOMethodInfo *info) 258 { 259 if (!(id_ == info->GetMethodId())) { 260 LOG_ECMA(ERROR) << "The method id must same for merging"; 261 return; 262 } 263 count_ += info->GetCount(); 264 SetSampleMode(info->GetSampleMode()); 265 } 266 GetMethodId()267 EntityId GetMethodId() const 268 { 269 return id_; 270 } 271 GetCount()272 uint32_t GetCount() const 273 { 274 return count_; 275 } 276 GetMethodName()277 const char *GetMethodName() const 278 { 279 return &methodName_; 280 } 281 SetSampleMode(SampleMode mode)282 void SetSampleMode(SampleMode mode) 283 { 284 if (mode_ == SampleMode::HOTNESS_MODE) { 285 return; 286 } 287 mode_ = mode; 288 } 289 GetSampleMode()290 SampleMode GetSampleMode() const 291 { 292 return mode_; 293 } 294 GetSampleModeToString()295 std::string GetSampleModeToString() const 296 { 297 std::string result; 298 switch (mode_) { 299 case SampleMode::HOTNESS_MODE: 300 result = "HOTNESS_MODE"; 301 break; 302 case SampleMode::CALL_MODE: 303 result = "CALL_MODE"; 304 break; 305 default: 306 LOG_ECMA(ERROR) << "mode error"; 307 } 308 return result; 309 } 310 IsFilter(uint32_t threshold)311 bool IsFilter(uint32_t threshold) const 312 { 313 if (count_ < threshold && mode_ == SampleMode::CALL_MODE) { 314 return true; 315 } 316 return false; 317 } 318 319 void ParseFromBinary(void **buffer); 320 void ProcessToBinary(std::ofstream &fileStream) const; 321 322 static std::vector<std::string> ParseFromText(const std::string &infoString); 323 void ProcessToText(std::string &text) const; 324 325 NO_COPY_SEMANTIC(PGOMethodInfo); 326 NO_MOVE_SEMANTIC(PGOMethodInfo); 327 328 private: 329 uint32_t size_ {0}; 330 EntityId id_; 331 uint32_t count_ {0}; 332 SampleMode mode_ {SampleMode::CALL_MODE}; 333 char methodName_; 334 }; 335 336 class PGOMethodInfoMap { 337 public: 338 PGOMethodInfoMap() = default; 339 Clear()340 void Clear() 341 { 342 // PGOMethodInfo release by chunk 343 methodInfos_.clear(); 344 } 345 346 bool AddMethod(Chunk *chunk, EntityId methodId, const CString &methodName, SampleMode mode); 347 void Merge(Chunk *chunk, PGOMethodInfoMap *methodInfos); 348 349 bool ParseFromBinary(uint32_t threshold, void **buffer); 350 bool ProcessToBinary(uint32_t threshold, const CString &recordName, const SaveTask *task, 351 std::ofstream &fileStream) const; 352 353 bool ParseFromText(Chunk *chunk, uint32_t threshold, const std::vector<std::string> &content); 354 void ProcessToText(uint32_t threshold, const CString &recordName, std::ofstream &stream) const; 355 356 NO_COPY_SEMANTIC(PGOMethodInfoMap); 357 NO_MOVE_SEMANTIC(PGOMethodInfoMap); 358 359 private: 360 CMap<EntityId, PGOMethodInfo *> methodInfos_; 361 }; 362 363 class PGOMethodIdSet { 364 public: 365 PGOMethodIdSet() = default; 366 Match(EntityId methodId)367 bool Match(EntityId methodId) 368 { 369 return methodIdSet_.find(methodId) != methodIdSet_.end(); 370 } 371 372 template <typename Callback> Update(const CString & recordName,Callback callback)373 bool Update(const CString &recordName, Callback callback) 374 { 375 std::unordered_set<EntityId> newIds = callback(recordName, methodIdSet_); 376 if (!newIds.empty()) { 377 methodIdSet_.insert(newIds.begin(), newIds.end()); 378 return true; 379 } 380 return false; 381 } 382 383 bool ParseFromBinary(uint32_t threshold, void **buffer); 384 385 NO_COPY_SEMANTIC(PGOMethodIdSet); 386 NO_MOVE_SEMANTIC(PGOMethodIdSet); 387 private: 388 std::unordered_set<EntityId> methodIdSet_; 389 }; 390 391 class PGORecordDetailInfos { 392 public: PGORecordDetailInfos(uint32_t hotnessThreshold)393 explicit PGORecordDetailInfos(uint32_t hotnessThreshold) : hotnessThreshold_(hotnessThreshold) 394 { 395 chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_); 396 }; 397 ~PGORecordDetailInfos()398 ~PGORecordDetailInfos() 399 { 400 Clear(); 401 } 402 Clear()403 void Clear() 404 { 405 for (auto iter : recordInfos_) { 406 iter.second->Clear(); 407 nativeAreaAllocator_.Delete(iter.second); 408 } 409 recordInfos_.clear(); 410 chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_); 411 } 412 413 // If it is a new method, return true. 414 bool AddMethod(const CString &recordName, EntityId methodId, const CString &methodName, SampleMode mode); 415 void Merge(const PGORecordDetailInfos &recordInfos); 416 417 void ParseFromBinary(void *buffer, SectionInfo *const info); 418 void ProcessToBinary(const SaveTask *task, std::ofstream &fileStream, SectionInfo *info) const; 419 420 bool ParseFromText(std::ifstream &stream); 421 void ProcessToText(std::ofstream &stream) const; 422 423 NO_COPY_SEMANTIC(PGORecordDetailInfos); 424 NO_MOVE_SEMANTIC(PGORecordDetailInfos); 425 private: 426 uint32_t hotnessThreshold_ {2}; 427 NativeAreaAllocator nativeAreaAllocator_; 428 std::unique_ptr<Chunk> chunk_; 429 CMap<CString, PGOMethodInfoMap *> recordInfos_; 430 }; 431 432 class PGORecordSimpleInfos { 433 public: PGORecordSimpleInfos(uint32_t threshold)434 explicit PGORecordSimpleInfos(uint32_t threshold) : hotnessThreshold_(threshold) {} ~PGORecordSimpleInfos()435 ~PGORecordSimpleInfos() 436 { 437 Clear(); 438 } 439 Clear()440 void Clear() 441 { 442 for (auto iter : methodIds_) { 443 nativeAreaAllocator_.Delete(iter.second); 444 } 445 methodIds_.clear(); 446 } 447 448 bool Match(const CString &recordName, EntityId methodId); 449 template <typename Callback> Update(Callback callback)450 void Update(Callback callback) 451 { 452 for (auto iter = methodIds_.begin(); iter != methodIds_.end(); iter++) { 453 auto recordName = iter->first; 454 auto methodIds = iter->second; 455 methodIds->Update(recordName, callback); 456 } 457 } 458 459 template <typename Callback> Update(const CString & recordName,Callback callback)460 void Update(const CString &recordName, Callback callback) 461 { 462 auto iter = methodIds_.find(recordName); 463 if (iter != methodIds_.end()) { 464 iter->second->Update(recordName, callback); 465 } else { 466 PGOMethodIdSet *methodIds = nativeAreaAllocator_.New<PGOMethodIdSet>(); 467 if (methodIds->Update(recordName, callback)) { 468 methodIds_.emplace(recordName, methodIds); 469 } else { 470 nativeAreaAllocator_.Delete(methodIds); 471 } 472 } 473 } 474 475 void ParseFromBinary(void *buffer, SectionInfo *const info); 476 477 NO_COPY_SEMANTIC(PGORecordSimpleInfos); 478 NO_MOVE_SEMANTIC(PGORecordSimpleInfos); 479 private: 480 uint32_t hotnessThreshold_ {2}; 481 NativeAreaAllocator nativeAreaAllocator_; 482 CUnorderedMap<CString, PGOMethodIdSet *> methodIds_; 483 }; 484 } // namespace panda::ecmascript 485 #endif // ECMASCRIPT_PGO_PROFILER_INFO_H 486