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 <cstdint> 20 #include <memory> 21 #include <sstream> 22 #include <unordered_map> 23 #include <unordered_set> 24 #include <string.h> 25 26 #include "ecmascript/base/file_header.h" 27 #include "ecmascript/jspandafile/method_literal.h" 28 #include "ecmascript/log_wrapper.h" 29 #include "ecmascript/mem/c_containers.h" 30 #include "ecmascript/mem/c_string.h" 31 #include "ecmascript/mem/chunk_containers.h" 32 #include "ecmascript/mem/native_area_allocator.h" 33 #include "ecmascript/mem/slots.h" 34 #include "ecmascript/pgo_profiler/pgo_profiler_layout.h" 35 #include "ecmascript/property_attributes.h" 36 37 38 namespace panda::ecmascript { 39 class SaveTask; 40 41 enum class SampleMode : uint8_t { 42 HOTNESS_MODE, 43 CALL_MODE, 44 }; 45 46 struct SectionInfo { 47 uint32_t offset_ {0}; 48 // reserve 49 uint32_t size_ {0}; 50 uint32_t number_ {0}; 51 }; 52 static constexpr size_t ALIGN_SIZE = 4; 53 using PGOMethodId = EntityId; 54 55 /** 56 |----PGOProfilerHeader 57 |--------MAGIC(8) 58 |------------{ 'P', 'A', 'N', 'D', 'A', '\0', '\0', '\0' } 59 |--------VERSION(4) 60 |------------{ '0', '0', '0', '0' } 61 |--------CHECKSUM(4) 62 |------------{ checksum } 63 |--------FILE_SIZE(4) 64 |------------{ fileSize } 65 |--------HEADER_SIZE(4) 66 |------------{ headerSize, from MAGIC to SECTION_NUMBER } 67 |--------ENDIAN_TAG(4) 68 |------------{ ENDIAN_TAG } 69 |--------SECTION_NUMBER(4) 70 |------------{ 3 } 71 |--------PANDA_FILE_INFO_SECTION_INFO(12) 72 |------------{ offset, size (reserve), number1 } 73 |--------RECORD_INFO_SECTION_INFO(12) 74 |------------{ offset, size (reserve), number2 } 75 |--------LAYOUT_DESC_SECTION_INFO(12) 76 |------------{ offset, size (reserve), number3 } 77 | 78 |----Section1: PGOPandaFileInfos(number1) 79 |--------[{ size, CHECK_SUM }, { size, CHECK_SUM }, ...] 80 | 81 |----Section2: PGORecordDetailInfos(number2) 82 |--------[ PGOMethodInfoMap(number4) 83 |------------{ offset, size, number4 } 84 |------------[ PGOMethodInfo(size1) 85 |----------------{ size1, entityId, count, mode, methodName, [{ size, offset, type }, { size, offset, type }, ...]}, 86 |------------ PGOMethodInfo(size1) 87 |----------------{ size1, entityId, count, mode, methodName, [{ size, offset, type }, { size, offset, type }, ...]}, 88 |--------------... ] 89 |-------- PGOMethodInfoMap() 90 |--------... ] 91 | 92 |----Section3: PGHClassLayoutDescs(number3) 93 |--------{ offset, size, number5 } 94 |--------[ PGOHClassLayoutDescInner(size) 95 |------------{ size, type, superType, count, ptCount, ctorCount, [{ size, handle, key }, { size, heandle, key }, ...]} 96 |-------- PGOHClassLayoutDescInner(size) 97 |------------{ size, type, superType, count, ptCount, ctorCount, [{ size, handle, key }, { size, heandle, key }, ...]} 98 */ 99 class PGOProfilerHeader : public base::FileHeaderElastic { 100 public: 101 static constexpr VersionType TYPE_MINI_VERSION = {0, 0, 0, 2}; 102 static constexpr VersionType METHOD_CHECKSUM_MINI_VERSION = {0, 0, 0, 4}; 103 static constexpr VersionType USE_HCLASS_TYPE_MINI_VERSION = {0, 0, 0, 5}; 104 static constexpr VersionType FILE_CONSISTENCY_MINI_VERSION = {0, 0, 0, 6}; 105 static constexpr VersionType TRACK_FIELD_MINI_VERSION = {0, 0, 0, 7}; 106 static constexpr VersionType ELEMENTS_KIND_MINI_VERSION = {0, 0, 0, 8}; 107 static constexpr VersionType FILE_SIZE_MINI_VERSION = FILE_CONSISTENCY_MINI_VERSION; 108 static constexpr VersionType HEADER_SIZE_MINI_VERSION = FILE_CONSISTENCY_MINI_VERSION; 109 static constexpr VersionType ELASTIC_HEADER_MINI_VERSION = FILE_CONSISTENCY_MINI_VERSION; 110 static constexpr VersionType LAST_VERSION = {0, 0, 0, 8}; 111 static constexpr size_t SECTION_SIZE = 3; 112 static constexpr size_t PANDA_FILE_SECTION_INDEX = 0; 113 static constexpr size_t RECORD_INFO_SECTION_INDEX = 1; 114 static constexpr size_t LAYOUT_DESC_SECTION_INDEX = 2; 115 PGOProfilerHeader()116 PGOProfilerHeader() : base::FileHeaderElastic(LAST_VERSION), sectionNumber_(SECTION_SIZE) 117 { 118 GetPandaInfoSection()->offset_ = Size(); 119 SetHeaderSize(Size()); 120 } 121 LastSize()122 static size_t LastSize() 123 { 124 return sizeof(PGOProfilerHeader) + (SECTION_SIZE - 1) * sizeof(SectionInfo); 125 } 126 Size(uint32_t sectionNumber)127 static size_t Size(uint32_t sectionNumber) 128 { 129 return sizeof(PGOProfilerHeader) + (sectionNumber - 1) * sizeof(SectionInfo); 130 } 131 Size()132 size_t Size() const 133 { 134 return Size(sectionNumber_); 135 } 136 Verify()137 bool Verify() const 138 { 139 return VerifyVersion("apPath file", LAST_VERSION, false); 140 } 141 Verify(void * buffer,size_t bufferSize)142 bool Verify(void *buffer, size_t bufferSize) const 143 { 144 if (!Verify()) { 145 return false; 146 } 147 if (!VerifyConsistency(buffer, bufferSize)) { 148 return false; 149 } 150 if (!VerifyFileSize(bufferSize)) { 151 return false; 152 } 153 return true; 154 } 155 156 bool VerifyFileSize(size_t bufferSize) const; 157 158 bool VerifyConsistency(void *buffer, size_t bufferSize) const; 159 Build(PGOProfilerHeader ** header,size_t size)160 static void Build(PGOProfilerHeader **header, size_t size) 161 { 162 *header = reinterpret_cast<PGOProfilerHeader *>(malloc(size)); 163 new (*header) PGOProfilerHeader(); 164 } 165 Destroy(PGOProfilerHeader ** header)166 static void Destroy(PGOProfilerHeader **header) 167 { 168 if (*header != nullptr) { 169 free(*header); 170 *header = nullptr; 171 } 172 } 173 174 // Copy Header. 175 static bool ParseFromBinary(void *buffer, size_t bufferSize, PGOProfilerHeader **header); 176 void ProcessToBinary(std::fstream &fileStream) const; 177 178 bool ParseFromText(std::ifstream &stream); 179 bool ProcessToText(std::ofstream &stream) const; 180 GetPandaInfoSection()181 SectionInfo *GetPandaInfoSection() const 182 { 183 return GetSectionInfo(PANDA_FILE_SECTION_INDEX); 184 } 185 GetRecordInfoSection()186 SectionInfo *GetRecordInfoSection() const 187 { 188 return GetSectionInfo(RECORD_INFO_SECTION_INDEX); 189 } 190 GetLayoutDescSection()191 SectionInfo *GetLayoutDescSection() const 192 { 193 return GetSectionInfo(LAYOUT_DESC_SECTION_INDEX); 194 } 195 SupportType()196 bool SupportType() const 197 { 198 return CompatibleVerify(TYPE_MINI_VERSION); 199 } 200 SupportMethodChecksum()201 bool SupportMethodChecksum() const 202 { 203 return CompatibleVerify(METHOD_CHECKSUM_MINI_VERSION); 204 } 205 SupportUseHClassType()206 bool SupportUseHClassType() const 207 { 208 return CompatibleVerify(USE_HCLASS_TYPE_MINI_VERSION); 209 } 210 SupportFileConsistency()211 bool SupportFileConsistency() const 212 { 213 return CompatibleVerify(FILE_CONSISTENCY_MINI_VERSION); 214 } 215 SupportFileSize()216 bool SupportFileSize() const 217 { 218 return CompatibleVerify(FILE_SIZE_MINI_VERSION); 219 } 220 SupportHeaderSize()221 bool SupportHeaderSize() const 222 { 223 return CompatibleVerify(HEADER_SIZE_MINI_VERSION); 224 } 225 SupportTrackField()226 bool SupportTrackField() const 227 { 228 return CompatibleVerify(TRACK_FIELD_MINI_VERSION); 229 } 230 SupportElementsKind()231 bool SupportElementsKind() const 232 { 233 return CompatibleVerify(ELEMENTS_KIND_MINI_VERSION); 234 } 235 236 NO_COPY_SEMANTIC(PGOProfilerHeader); 237 NO_MOVE_SEMANTIC(PGOProfilerHeader); 238 239 private: 240 static bool BuildFromLegacy(void *buffer, PGOProfilerHeader **header); 241 static bool BuildFromElastic(void *buffer, size_t bufferSize, PGOProfilerHeader **header); 242 GetSectionInfo(size_t index)243 SectionInfo *GetSectionInfo(size_t index) const 244 { 245 if (index >= sectionNumber_) { 246 return nullptr; 247 } 248 return const_cast<SectionInfo *>(§ionInfos_) + index; 249 } 250 251 uint32_t sectionNumber_ {SECTION_SIZE}; 252 SectionInfo sectionInfos_; 253 }; 254 255 class PGOProfilerHeaderLegacy : public base::FileHeaderBase { 256 public: 257 static constexpr size_t SECTION_SIZE = 3; 258 static constexpr VersionType LAST_VERSION = {0, 0, 0, 5}; PGOProfilerHeaderLegacy()259 PGOProfilerHeaderLegacy() : base::FileHeaderBase(LAST_VERSION), sectionNumber_(SECTION_SIZE) {}; 260 GetSectionNumber()261 const uint32_t& GetSectionNumber () const 262 { 263 return sectionNumber_; 264 } 265 266 private: 267 uint32_t sectionNumber_ {SECTION_SIZE}; 268 SectionInfo sectionInfos_; 269 }; 270 271 class PGOPandaFileInfos { 272 public: Sample(uint32_t checksum)273 void Sample(uint32_t checksum) 274 { 275 fileInfos_.emplace(checksum); 276 } 277 Clear()278 void Clear() 279 { 280 fileInfos_.clear(); 281 } 282 283 void ParseFromBinary(void *buffer, SectionInfo *const info); 284 void ProcessToBinary(std::fstream &fileStream, SectionInfo *info) const; 285 void Merge(const PGOPandaFileInfos &pandaFileInfos); 286 bool VerifyChecksum(const PGOPandaFileInfos &pandaFileInfos, const std::string &base, 287 const std::string &incoming) const; 288 289 void ProcessToText(std::ofstream &stream) const; 290 bool ParseFromText(std::ifstream &stream); 291 292 bool Checksum(uint32_t checksum) const; 293 294 private: 295 class FileInfo { 296 public: 297 FileInfo() = default; FileInfo(uint32_t checksum)298 FileInfo(uint32_t checksum) : size_(LastSize()), checksum_(checksum) {} 299 LastSize()300 static size_t LastSize() 301 { 302 return sizeof(FileInfo); 303 } 304 Size()305 size_t Size() const 306 { 307 return static_cast<size_t>(size_); 308 } 309 310 bool operator<(const FileInfo &right) const 311 { 312 return checksum_ < right.checksum_; 313 } 314 GetChecksum()315 uint32_t GetChecksum() const 316 { 317 return checksum_; 318 } 319 320 private: 321 // Support extended fields 322 uint32_t size_; 323 uint32_t checksum_; 324 }; 325 326 std::set<FileInfo> fileInfos_; 327 }; 328 329 class PGOMethodInfo { 330 public: 331 static constexpr int METHOD_INFO_COUNT = 4; 332 static constexpr int METHOD_ID_INDEX = 0; 333 static constexpr int METHOD_COUNT_INDEX = 1; 334 static constexpr int METHOD_MODE_INDEX = 2; 335 static constexpr int METHOD_NAME_INDEX = 3; 336 static constexpr uint32_t METHOD_MAX_HIT_COUNT = 10000U; 337 PGOMethodInfo(PGOMethodId id)338 explicit PGOMethodInfo(PGOMethodId id) : id_(id) {} 339 PGOMethodInfo(PGOMethodId id,uint32_t count,SampleMode mode,const char * methodName)340 PGOMethodInfo(PGOMethodId id, uint32_t count, SampleMode mode, const char *methodName) 341 : id_(id), count_(count), mode_(mode) 342 { 343 size_t len = strlen(methodName); 344 size_ = static_cast<uint32_t>(Size(len)); 345 if (len > 0 && memcpy_s(&methodName_, len, methodName, len) != EOK) { 346 LOG_ECMA(ERROR) << "SetMethodName memcpy_s failed" << methodName << ", len = " << len; 347 UNREACHABLE(); 348 } 349 *(&methodName_ + len) = '\0'; 350 } 351 352 static uint32_t CalcChecksum(const char *name, const uint8_t *byteCodeArray, uint32_t byteCodeLength); 353 354 static uint32_t CalcOpCodeChecksum(const uint8_t *byteCodeArray, uint32_t byteCodeLength); 355 Size(uint32_t length)356 static int32_t Size(uint32_t length) 357 { 358 return sizeof(PGOMethodInfo) + AlignUp(length, ALIGN_SIZE); 359 } 360 Size()361 int32_t Size() const 362 { 363 return size_; 364 } 365 GetSampleMode(std::string content,SampleMode & mode)366 static bool GetSampleMode(std::string content, SampleMode &mode) 367 { 368 if (content == "HOTNESS_MODE") { 369 mode = SampleMode::HOTNESS_MODE; 370 } else if (content == "CALL_MODE") { 371 mode = SampleMode::CALL_MODE; 372 } else { 373 return false; 374 } 375 return true; 376 } 377 IncreaseCount(int32_t inc)378 void IncreaseCount(int32_t inc) 379 { 380 count_ += static_cast<uint32_t>(inc); 381 } 382 ClearCount()383 void ClearCount() 384 { 385 count_ = 0; 386 } 387 Merge(const PGOMethodInfo * info)388 void Merge(const PGOMethodInfo *info) 389 { 390 if (!(id_ == info->GetMethodId())) { 391 LOG_ECMA(ERROR) << "The method id must same for merging"; 392 return; 393 } 394 count_ = std::min(count_ + info->GetCount(), METHOD_MAX_HIT_COUNT); 395 SetSampleMode(info->GetSampleMode()); 396 } 397 GetMethodId()398 PGOMethodId GetMethodId() const 399 { 400 return id_; 401 } 402 GetCount()403 uint32_t GetCount() const 404 { 405 return count_; 406 } 407 GetMethodName()408 const char *GetMethodName() const 409 { 410 return &methodName_; 411 } 412 SetSampleMode(SampleMode mode)413 void SetSampleMode(SampleMode mode) 414 { 415 if (mode_ == SampleMode::HOTNESS_MODE) { 416 return; 417 } 418 mode_ = mode; 419 } 420 GetSampleMode()421 SampleMode GetSampleMode() const 422 { 423 return mode_; 424 } 425 GetSampleModeToString()426 std::string GetSampleModeToString() const 427 { 428 std::string result; 429 switch (mode_) { 430 case SampleMode::HOTNESS_MODE: 431 result = "HOTNESS_MODE"; 432 break; 433 case SampleMode::CALL_MODE: 434 result = "CALL_MODE"; 435 break; 436 default: 437 LOG_ECMA(ERROR) << "mode error"; 438 } 439 return result; 440 } 441 IsFilter(uint32_t threshold)442 bool IsFilter(uint32_t threshold) const 443 { 444 if (count_ < threshold && mode_ == SampleMode::CALL_MODE) { 445 return true; 446 } 447 return false; 448 } 449 450 void ParseFromBinary(void **buffer); 451 void ProcessToBinary(std::ofstream &fileStream) const; 452 453 static std::vector<std::string> ParseFromText(const std::string &infoString); 454 void ProcessToText(std::string &text) const; 455 456 NO_COPY_SEMANTIC(PGOMethodInfo); 457 NO_MOVE_SEMANTIC(PGOMethodInfo); 458 459 private: 460 uint32_t size_ {0}; 461 PGOMethodId id_; 462 uint32_t count_ {0}; 463 SampleMode mode_ {SampleMode::CALL_MODE}; 464 char methodName_ {0}; 465 }; 466 467 class PGOMethodTypeSet { 468 public: 469 static constexpr int METHOD_TYPE_INFO_INDEX = 4; 470 static constexpr int METHOD_TYPE_INFO_COUNT = 2; 471 static constexpr int METHOD_OFFSET_INDEX = 0; 472 static constexpr int METHOD_TYPE_INDEX = 1; 473 474 PGOMethodTypeSet() = default; 475 AddType(uint32_t offset,PGOSampleType type)476 void AddType(uint32_t offset, PGOSampleType type) 477 { 478 auto result = scalarOpTypeInfos_.find(ScalarOpTypeInfo(offset, type)); 479 if (result != scalarOpTypeInfos_.end()) { 480 auto combineType = result->GetType().CombineType(type); 481 const_cast<ScalarOpTypeInfo &>(*result).SetType(combineType); 482 } else { 483 scalarOpTypeInfos_.emplace(offset, type); 484 } 485 } 486 AddCallTargetType(uint32_t offset,PGOSampleType type)487 void AddCallTargetType(uint32_t offset, PGOSampleType type) 488 { 489 auto result = scalarOpTypeInfos_.find(ScalarOpTypeInfo(offset, type)); 490 if (result != scalarOpTypeInfos_.end()) { 491 auto combineType = result->GetType().CombineCallTargetType(type); 492 const_cast<ScalarOpTypeInfo &>(*result).SetType(combineType); 493 } else { 494 scalarOpTypeInfos_.emplace(offset, type); 495 } 496 } 497 AddObjectInfo(uint32_t offset,const PGOObjectInfo & info)498 void AddObjectInfo(uint32_t offset, const PGOObjectInfo &info) 499 { 500 auto result = rwScalarOpTypeInfos_.find(RWScalarOpTypeInfo(offset)); 501 if (result != rwScalarOpTypeInfos_.end()) { 502 const_cast<RWScalarOpTypeInfo &>(*result).AddObjectInfo(info); 503 } else { 504 rwScalarOpTypeInfos_.emplace(offset, info); 505 } 506 } 507 AddDefine(uint32_t offset,PGOSampleType type,PGOSampleType superType)508 void AddDefine(uint32_t offset, PGOSampleType type, PGOSampleType superType) 509 { 510 auto result = objDefOpTypeInfos_.find(ObjDefOpTypeInfo(offset, type, superType)); 511 if (result != objDefOpTypeInfos_.end()) { 512 return; 513 } 514 objDefOpTypeInfos_.emplace(offset, type, superType); 515 } 516 517 template <typename Callback> GetTypeInfo(Callback callback)518 void GetTypeInfo(Callback callback) 519 { 520 for (const auto &typeInfo : scalarOpTypeInfos_) { 521 auto type = typeInfo.GetType(); 522 callback(typeInfo.GetOffset(), &type); 523 } 524 for (const auto &typeInfo : rwScalarOpTypeInfos_) { 525 auto type = typeInfo.GetType(); 526 callback(typeInfo.GetOffset(), &type); 527 } 528 for (const auto &typeInfo : objDefOpTypeInfos_) { 529 auto classType = typeInfo.GetType(); 530 callback(typeInfo.GetOffset(), &classType); 531 } 532 } 533 534 void Merge(const PGOMethodTypeSet *info); 535 static void SkipFromBinary(void **buffer); 536 537 bool ParseFromBinary(void **buffer, PGOProfilerHeader *const header); 538 bool ProcessToBinary(std::stringstream &stream) const; 539 540 bool ParseFromText(const std::string &typeString); 541 void ProcessToText(std::string &text) const; 542 543 NO_COPY_SEMANTIC(PGOMethodTypeSet); 544 NO_MOVE_SEMANTIC(PGOMethodTypeSet); 545 546 private: 547 enum class InfoType : uint8_t { NONE, OP_TYPE, DEFINE_CLASS_TYPE = 3, USE_HCLASS_TYPE }; 548 549 class TypeInfoHeader { 550 public: TypeInfoHeader(InfoType type,uint32_t offset)551 TypeInfoHeader(InfoType type, uint32_t offset) : infoType_(type), offset_(offset) {} TypeInfoHeader(uint32_t size,InfoType type,uint32_t offset)552 TypeInfoHeader(uint32_t size, InfoType type, uint32_t offset) 553 : size_(size), infoType_(type), offset_(offset) {} 554 GetInfoType()555 InfoType GetInfoType() 556 { 557 return infoType_; 558 } 559 Size()560 int32_t Size() const 561 { 562 return size_; 563 } 564 GetOffset()565 uint32_t GetOffset() const 566 { 567 return offset_; 568 } 569 570 protected: 571 uint32_t size_ {0}; 572 InfoType infoType_ {InfoType::NONE}; 573 uint32_t offset_ {0}; 574 }; 575 576 class RWScalarOpTypeInfo : public TypeInfoHeader { 577 public: RWScalarOpTypeInfo(uint32_t offset)578 explicit RWScalarOpTypeInfo(uint32_t offset) 579 : TypeInfoHeader(InfoType::USE_HCLASS_TYPE, offset) {}; RWScalarOpTypeInfo(uint32_t offset,PGOObjectInfo info)580 RWScalarOpTypeInfo(uint32_t offset, PGOObjectInfo info) 581 : TypeInfoHeader(sizeof(RWScalarOpTypeInfo), InfoType::USE_HCLASS_TYPE, offset) 582 { 583 type_.AddObjectInfo(info); 584 } 585 586 bool operator<(const RWScalarOpTypeInfo &right) const 587 { 588 return offset_ < right.offset_; 589 } 590 GetCount()591 int32_t GetCount() 592 { 593 return type_.GetCount(); 594 } 595 Merge(const RWScalarOpTypeInfo & type)596 void Merge(const RWScalarOpTypeInfo &type) 597 { 598 type_.Merge(type.type_); 599 } 600 AddObjectInfo(const PGOObjectInfo & info)601 void AddObjectInfo(const PGOObjectInfo &info) 602 { 603 type_.AddObjectInfo(info); 604 } 605 GetType()606 PGORWOpType GetType() const 607 { 608 return type_; 609 } 610 611 void ProcessToText(std::string &text) const; 612 613 private: 614 PGORWOpType type_; 615 }; 616 617 class ScalarOpTypeInfo : public TypeInfoHeader { 618 public: ScalarOpTypeInfo(uint32_t offset,PGOSampleType type)619 ScalarOpTypeInfo(uint32_t offset, PGOSampleType type) 620 : TypeInfoHeader(sizeof(ScalarOpTypeInfo), InfoType::OP_TYPE, offset), type_(type) {} 621 622 bool operator<(const ScalarOpTypeInfo &right) const 623 { 624 return offset_ < right.offset_; 625 } 626 SetType(PGOSampleType type)627 void SetType(PGOSampleType type) 628 { 629 if (type_ != type) { 630 type_ = type; 631 } 632 } 633 Merge(const ScalarOpTypeInfo & typeInfo)634 void Merge(const ScalarOpTypeInfo &typeInfo) 635 { 636 PGOSampleType combineType = GetType().CombineType(typeInfo.GetType()); 637 SetType(combineType); 638 } 639 GetType()640 PGOSampleType GetType() const 641 { 642 return type_; 643 } 644 645 protected: ScalarOpTypeInfo(uint32_t size,InfoType infoType,uint32_t offset,PGOSampleType type)646 ScalarOpTypeInfo(uint32_t size, InfoType infoType, uint32_t offset, PGOSampleType type) 647 : TypeInfoHeader(size, infoType, offset), type_(type) {} 648 649 private: 650 PGOSampleType type_; 651 }; 652 653 class ObjDefOpTypeInfo : public ScalarOpTypeInfo { 654 public: ObjDefOpTypeInfo(uint32_t offset,PGOSampleType type,PGOSampleType superType)655 ObjDefOpTypeInfo(uint32_t offset, PGOSampleType type, PGOSampleType superType) 656 : ScalarOpTypeInfo(sizeof(ObjDefOpTypeInfo), InfoType::DEFINE_CLASS_TYPE, offset, type), 657 superType_(superType) {} 658 GetSuperType()659 PGOSampleType GetSuperType() const 660 { 661 return superType_; 662 } 663 664 bool operator<(const ObjDefOpTypeInfo &right) const 665 { 666 return offset_ < right.GetOffset() || GetType() < right.GetType() || superType_ < right.superType_; 667 } 668 669 void ProcessToText(std::string &text) const; 670 671 protected: ObjDefOpTypeInfo(uint32_t size,InfoType infoType,uint32_t offset,PGOSampleType type,PGOSampleType superType)672 ObjDefOpTypeInfo( 673 uint32_t size, InfoType infoType, uint32_t offset, PGOSampleType type, PGOSampleType superType) 674 : ScalarOpTypeInfo(size, infoType, offset, type), superType_(superType) {} 675 676 private: 677 PGOSampleType superType_; 678 }; 679 680 std::set<ScalarOpTypeInfo> scalarOpTypeInfos_; 681 std::set<RWScalarOpTypeInfo> rwScalarOpTypeInfos_; 682 std::set<ObjDefOpTypeInfo> objDefOpTypeInfos_; 683 }; 684 685 class PGODecodeMethodInfo { 686 public: PGODecodeMethodInfo(PGOMethodId id)687 explicit PGODecodeMethodInfo(PGOMethodId id) : methodId_(id) {} 688 GetMethodId()689 PGOMethodId GetMethodId() const 690 { 691 return methodId_; 692 } 693 GetPGOMethodTypeSet()694 PGOMethodTypeSet &GetPGOMethodTypeSet() 695 { 696 return pgoMethodTypeSet_; 697 } 698 699 void Merge(const PGODecodeMethodInfo &from); 700 701 private: 702 PGOMethodId methodId_ {0}; 703 PGOMethodTypeSet pgoMethodTypeSet_ {}; 704 }; 705 706 class PGOHClassLayoutDescInner { 707 public: PGOHClassLayoutDescInner(size_t size,PGOSampleType type,PGOSampleType superType,ElementsKind kind)708 PGOHClassLayoutDescInner(size_t size, PGOSampleType type, PGOSampleType superType, ElementsKind kind) 709 : size_(size), type_(type), superType_(superType) 710 { 711 SetElementsKind(kind); 712 } 713 714 static size_t CaculateSize(const PGOHClassLayoutDesc &desc); 715 static std::string GetTypeString(const PGOHClassLayoutDesc &desc); 716 717 void Merge(const PGOHClassLayoutDesc &desc); 718 Size()719 int32_t Size() const 720 { 721 return size_; 722 } 723 GetType()724 PGOSampleType GetType() const 725 { 726 return type_; 727 } 728 Convert(PGOProfilerHeader * const header)729 PGOHClassLayoutDesc Convert(PGOProfilerHeader *const header) 730 { 731 PGOHClassLayoutDesc desc(GetType().GetClassType()); 732 desc.SetSuperClassType(superType_.GetClassType()); 733 auto descInfo = GetFirst(); 734 for (int32_t i = 0; i < count_; i++) { 735 desc.AddKeyAndDesc(descInfo->GetKey(), descInfo->GetHandler()); 736 descInfo = GetNext(descInfo); 737 } 738 for (int32_t i = 0; i < ptCount_; i++) { 739 desc.AddPtKeyAndDesc(descInfo->GetKey(), descInfo->GetHandler()); 740 descInfo = GetNext(descInfo); 741 } 742 for (int32_t i = 0; i < ctorCount_; i++) { 743 desc.AddCtorKeyAndDesc(descInfo->GetKey(), descInfo->GetHandler()); 744 descInfo = GetNext(descInfo); 745 } 746 if (header->SupportElementsKind()) { 747 desc.SetElementsKind(GetElementsKind()); 748 } 749 return desc; 750 } 751 752 class PGOLayoutDescInfo { 753 public: 754 PGOLayoutDescInfo() = default; PGOLayoutDescInfo(const CString & key,PGOHandler handler)755 PGOLayoutDescInfo(const CString &key, PGOHandler handler) : handler_(handler) 756 { 757 size_t len = key.size(); 758 size_ = Size(len); 759 if (len > 0 && memcpy_s(&key_, len, key.c_str(), len) != EOK) { 760 LOG_ECMA(ERROR) << "SetMethodName memcpy_s failed" << key << ", len = " << len; 761 UNREACHABLE(); 762 } 763 *(&key_ + len) = '\0'; 764 } 765 Size(size_t len)766 static int32_t Size(size_t len) 767 { 768 return sizeof(PGOLayoutDescInfo) + AlignUp(len, ALIGN_SIZE); 769 } 770 Size()771 int32_t Size() const 772 { 773 return size_; 774 } 775 GetKey()776 const char *GetKey() const 777 { 778 return &key_; 779 } 780 GetHandler()781 PGOHandler GetHandler() const 782 { 783 return handler_; 784 } 785 786 private: 787 int32_t size_ {0}; 788 PGOHandler handler_; 789 char key_ {'\0'}; 790 }; 791 792 private: GetFirst()793 const PGOLayoutDescInfo *GetFirst() const 794 { 795 return &descInfos_; 796 } 797 GetNext(const PGOLayoutDescInfo * current)798 const PGOLayoutDescInfo *GetNext(const PGOLayoutDescInfo *current) const 799 { 800 return reinterpret_cast<PGOLayoutDescInfo *>(reinterpret_cast<uintptr_t>(current) + current->Size()); 801 } 802 SetElementsKind(ElementsKind kind)803 void SetElementsKind(ElementsKind kind) 804 { 805 *reinterpret_cast<ElementsKind *>(GetEnd() - sizeof(ElementsKind)) = kind; 806 } 807 GetElementsKind()808 ElementsKind GetElementsKind() const 809 { 810 return *reinterpret_cast<const ElementsKind *>(GetEnd() - sizeof(ElementsKind)); 811 } 812 GetEnd()813 uintptr_t GetEnd() const 814 { 815 return reinterpret_cast<uintptr_t>(this) + Size(); 816 } 817 818 int32_t size_; 819 PGOSampleType type_; 820 PGOSampleType superType_; 821 int32_t count_ {0}; 822 int32_t ptCount_ {0}; 823 int32_t ctorCount_ {0}; 824 PGOLayoutDescInfo descInfos_; 825 }; 826 827 class PGOMethodInfoMap { 828 public: 829 PGOMethodInfoMap() = default; 830 Clear()831 void Clear() 832 { 833 // PGOMethodInfo release by chunk 834 methodInfos_.clear(); 835 methodTypeInfos_.clear(); 836 } 837 838 bool AddMethod(Chunk *chunk, Method *jsMethod, SampleMode mode, int32_t incCount); 839 bool AddType(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type); 840 bool AddCallTargetType(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type); 841 bool AddObjectInfo(Chunk *chunk, PGOMethodId methodId, int32_t offset, const PGOObjectInfo &info); 842 bool AddDefine(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type, PGOSampleType superType); 843 void Merge(Chunk *chunk, PGOMethodInfoMap *methodInfos); 844 845 bool ParseFromBinary(Chunk *chunk, uint32_t threshold, void **buffer, PGOProfilerHeader *const header); 846 bool ProcessToBinary(uint32_t threshold, const CString &recordName, const SaveTask *task, std::fstream &fileStream, 847 PGOProfilerHeader *const header) const; 848 849 bool ParseFromText(Chunk *chunk, uint32_t threshold, const std::vector<std::string> &content); 850 void ProcessToText(uint32_t threshold, const CString &recordName, std::ofstream &stream) const; 851 GetMethodInfos()852 const CMap<PGOMethodId, PGOMethodInfo *> &GetMethodInfos() const 853 { 854 return methodInfos_; 855 } 856 857 NO_COPY_SEMANTIC(PGOMethodInfoMap); 858 NO_MOVE_SEMANTIC(PGOMethodInfoMap); 859 860 private: 861 PGOMethodTypeSet *GetOrInsertMethodTypeSet(Chunk *chunk, PGOMethodId methodId); 862 863 CMap<PGOMethodId, PGOMethodInfo *> methodInfos_; 864 CMap<PGOMethodId, PGOMethodTypeSet *> methodTypeInfos_; 865 CMap<PGOMethodId, uint32_t> methodsChecksum_; 866 CMap<PGOSampleType, CMap<CString, TrackType>> globalLayoutDescInfos_; 867 }; 868 869 class PGOMethodIdSet { 870 public: PGOMethodIdSet(Chunk * chunk)871 explicit PGOMethodIdSet(Chunk* chunk): chunk_(chunk), methodInfoMap_(chunk) {}; 872 ~PGOMethodIdSet() = default; 873 Clear()874 void Clear() 875 { 876 candidateSet_.clear(); 877 for (auto &methodNameSet : methodInfoMap_) { 878 methodNameSet.second.Clear(); 879 } 880 methodInfoMap_.clear(); 881 } 882 Match(EntityId methodId)883 bool Match(EntityId methodId) 884 { 885 return candidateSet_.find(methodId) != candidateSet_.end(); 886 } 887 888 template <typename Callback> Update(const CString & recordName,Callback callback)889 bool Update(const CString &recordName, Callback callback) 890 { 891 std::unordered_set<EntityId> newIds = callback(recordName, candidateSet_); 892 if (!newIds.empty()) { 893 candidateSet_.insert(newIds.begin(), newIds.end()); 894 return true; 895 } 896 return false; 897 } 898 899 template <typename Callback> GetTypeInfo(const char * methodName,Callback callback)900 void GetTypeInfo(const char *methodName, Callback callback) 901 { 902 // for no function checksum in ap file 903 auto iter = methodInfoMap_.find(methodName); 904 if ((iter != methodInfoMap_.end()) && (iter->second.GetFirstMethodInfo() != nullptr)) { 905 iter->second.GetFirstMethodInfo()->GetPGOMethodTypeSet().GetTypeInfo(callback); 906 } 907 } 908 909 template <typename Callback> GetTypeInfo(const char * methodName,uint32_t checksum,Callback callback)910 void GetTypeInfo(const char *methodName, uint32_t checksum, Callback callback) 911 { 912 auto iter = methodInfoMap_.find(methodName); 913 if ((iter != methodInfoMap_.end()) && (iter->second.GetMethodInfo(checksum) != nullptr)) { 914 return iter->second.GetMethodInfo(checksum)->GetPGOMethodTypeSet().GetTypeInfo(callback); 915 } 916 LOG_ECMA(DEBUG) << "Method checksum mismatched, name: " << methodName; 917 } 918 MatchAndMarkMethod(const char * methodName,EntityId methodId)919 void MatchAndMarkMethod(const char *methodName, EntityId methodId) 920 { 921 const auto &iter = methodInfoMap_.find(methodName); 922 if (iter == methodInfoMap_.end()) { 923 // no matching method in PGO file. 924 return; 925 } 926 candidateSet_.emplace(methodId); 927 iter->second.SetMatch(); 928 } 929 930 bool ParseFromBinary(uint32_t threshold, void **buffer, PGOProfilerHeader *const header); 931 932 void GetMismatchResult(const CString &recordName, uint32_t &totalMethodCount, uint32_t &mismatchMethodCount, 933 std::set<std::pair<std::string, CString>> &mismatchMethodSet) const; 934 935 void Merge(const PGOMethodIdSet &from); 936 937 class PGOMethodNameSet { 938 public: PGOMethodNameSet(Chunk * chunk)939 explicit PGOMethodNameSet(Chunk* chunk): methodMap_(chunk) {}; SetMatch()940 void SetMatch() 941 { 942 methodNameMatch_ = true; 943 } 944 IsMatch()945 bool IsMatch() const 946 { 947 return methodNameMatch_; 948 } 949 GetOrCreateMethodInfo(uint32_t checksum,PGOMethodId methodId)950 PGODecodeMethodInfo& GetOrCreateMethodInfo(uint32_t checksum, PGOMethodId methodId) 951 { 952 auto methodIter = methodMap_.find(checksum); 953 if (methodIter == methodMap_.end()) { 954 auto ret = methodMap_.emplace(checksum, methodId); 955 ASSERT(ret.second); 956 methodIter = ret.first; 957 } 958 return methodIter->second; 959 } 960 Merge(const PGOMethodNameSet & from)961 void Merge(const PGOMethodNameSet &from) 962 { 963 for (const auto &method : from.methodMap_) { 964 uint32_t checksum = method.first; 965 auto methodInfo = methodMap_.find(checksum); 966 if (methodInfo == methodMap_.end()) { 967 auto ret = methodMap_.emplace(checksum, method.second.GetMethodId()); 968 ASSERT(ret.second); 969 methodInfo = ret.first; 970 } 971 methodInfo->second.Merge(method.second); 972 } 973 } 974 GetFirstMethodInfo()975 PGODecodeMethodInfo *GetFirstMethodInfo() 976 { 977 if (methodMap_.empty()) { 978 return nullptr; 979 } 980 return &(methodMap_.begin()->second); 981 } 982 GetMethodInfo(uint32_t checksum)983 PGODecodeMethodInfo *GetMethodInfo(uint32_t checksum) 984 { 985 auto methodInfo = methodMap_.find(checksum); 986 if (methodInfo == methodMap_.end()) { 987 return nullptr; 988 } 989 return &(methodInfo->second); 990 } 991 Clear()992 void Clear() 993 { 994 methodMap_.clear(); 995 } 996 997 private: 998 bool methodNameMatch_ {false}; 999 ChunkUnorderedMap<uint32_t, PGODecodeMethodInfo> methodMap_; 1000 }; 1001 1002 NO_COPY_SEMANTIC(PGOMethodIdSet); 1003 NO_MOVE_SEMANTIC(PGOMethodIdSet); 1004 1005 private: 1006 Chunk* chunk_; 1007 std::unordered_set<EntityId> candidateSet_; // methodId in abc file, DO NOT for pgo internal use 1008 ChunkUnorderedMap<CString, PGOMethodNameSet> methodInfoMap_; 1009 }; 1010 1011 class PGORecordDetailInfos { 1012 public: PGORecordDetailInfos(uint32_t hotnessThreshold)1013 explicit PGORecordDetailInfos(uint32_t hotnessThreshold) : hotnessThreshold_(hotnessThreshold) 1014 { 1015 chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_); 1016 }; 1017 ~PGORecordDetailInfos()1018 ~PGORecordDetailInfos() 1019 { 1020 Clear(); 1021 } 1022 Clear()1023 void Clear() 1024 { 1025 for (auto iter : recordInfos_) { 1026 iter.second->Clear(); 1027 nativeAreaAllocator_.Delete(iter.second); 1028 } 1029 recordInfos_.clear(); 1030 chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_); 1031 } 1032 1033 // If it is a new method, return true. 1034 bool AddMethod(const CString &recordName, Method *jsMethod, SampleMode mode, int32_t incCount); 1035 bool AddType(const CString &recordName, PGOMethodId methodId, int32_t offset, PGOSampleType type); 1036 bool AddCallTargetType(const CString &recordName, PGOMethodId methodId, int32_t offset, PGOSampleType type); 1037 bool AddObjectInfo(const CString &recordName, PGOMethodId methodId, int32_t offset, const PGOObjectInfo &info); 1038 bool AddDefine( 1039 const CString &recordName, PGOMethodId methodId, int32_t offset, PGOSampleType type, PGOSampleType superType); 1040 bool AddLayout(PGOSampleType type, JSTaggedType hclass, PGOObjKind kind); 1041 void Merge(const PGORecordDetailInfos &recordInfos); 1042 1043 void ParseFromBinary(void *buffer, PGOProfilerHeader *const header); 1044 void ProcessToBinary(const SaveTask *task, std::fstream &fileStream, PGOProfilerHeader *const header) const; 1045 1046 bool ParseFromText(std::ifstream &stream); 1047 void ProcessToText(std::ofstream &stream) const; 1048 GetRecordInfos()1049 const CMap<CString, PGOMethodInfoMap *> &GetRecordInfos() const 1050 { 1051 return recordInfos_; 1052 } 1053 1054 NO_COPY_SEMANTIC(PGORecordDetailInfos); 1055 NO_MOVE_SEMANTIC(PGORecordDetailInfos); 1056 1057 private: 1058 PGOMethodInfoMap *GetMethodInfoMap(const CString &recordName); 1059 bool ParseFromBinaryForLayout(void **buffer, PGOProfilerHeader *const header); 1060 bool ProcessToBinaryForLayout(NativeAreaAllocator *allocator, const SaveTask *task, std::fstream &stream) const; 1061 1062 uint32_t hotnessThreshold_ {2}; 1063 NativeAreaAllocator nativeAreaAllocator_; 1064 std::unique_ptr<Chunk> chunk_; 1065 CMap<CString, PGOMethodInfoMap *> recordInfos_; 1066 std::set<PGOHClassLayoutDesc> moduleLayoutDescInfos_; 1067 }; 1068 1069 class PGORecordSimpleInfos { 1070 public: PGORecordSimpleInfos(uint32_t threshold)1071 explicit PGORecordSimpleInfos(uint32_t threshold) : hotnessThreshold_(threshold) 1072 { 1073 chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_); 1074 } 1075 ~PGORecordSimpleInfos()1076 ~PGORecordSimpleInfos() 1077 { 1078 Clear(); 1079 } 1080 Clear()1081 void Clear() 1082 { 1083 for (const auto &iter : methodIds_) { 1084 iter.second->Clear(); 1085 nativeAreaAllocator_.Delete(iter.second); 1086 } 1087 methodIds_.clear(); 1088 chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_); 1089 } 1090 1091 bool Match(const CString &recordName, EntityId methodId); 1092 1093 template <typename Callback> Update(Callback callback)1094 void Update(Callback callback) 1095 { 1096 for (auto iter = methodIds_.begin(); iter != methodIds_.end(); iter++) { 1097 auto recordName = iter->first; 1098 auto methodIds = iter->second; 1099 methodIds->Update(recordName, callback); 1100 } 1101 } 1102 1103 template <typename Callback> Update(const CString & recordName,Callback callback)1104 void Update(const CString &recordName, Callback callback) 1105 { 1106 auto iter = methodIds_.find(recordName); 1107 if (iter != methodIds_.end()) { 1108 iter->second->Update(recordName, callback); 1109 } else { 1110 PGOMethodIdSet *methodIds = nativeAreaAllocator_.New<PGOMethodIdSet>(chunk_.get()); 1111 if (methodIds->Update(recordName, callback)) { 1112 methodIds_.emplace(recordName, methodIds); 1113 } else { 1114 nativeAreaAllocator_.Delete(methodIds); 1115 } 1116 } 1117 } 1118 1119 template <typename Callback> GetTypeInfo(const CString & recordName,const char * methodName,Callback callback)1120 void GetTypeInfo(const CString &recordName, const char *methodName, Callback callback) 1121 { 1122 auto iter = methodIds_.find(recordName); 1123 if (iter != methodIds_.end()) { 1124 iter->second->GetTypeInfo(methodName, callback); 1125 } 1126 } 1127 1128 template <typename Callback> GetTypeInfo(const CString & recordName,const char * methodName,uint32_t checksum,Callback callback)1129 void GetTypeInfo(const CString &recordName, const char *methodName, uint32_t checksum, Callback callback) 1130 { 1131 auto iter = methodIds_.find(recordName); 1132 if (iter != methodIds_.end()) { 1133 iter->second->GetTypeInfo(methodName, checksum, callback); 1134 } 1135 } 1136 GetHClassLayoutDesc(PGOSampleType classType,PGOHClassLayoutDesc ** desc)1137 bool GetHClassLayoutDesc(PGOSampleType classType, PGOHClassLayoutDesc **desc) const 1138 { 1139 auto iter = moduleLayoutDescInfos_.find(PGOHClassLayoutDesc(classType.GetClassType())); 1140 if (iter != moduleLayoutDescInfos_.end()) { 1141 *desc = &(const_cast<PGOHClassLayoutDesc &>(*iter)); 1142 return true; 1143 } 1144 return false; 1145 } 1146 MatchAndMarkMethod(const CString & recordName,const char * methodName,EntityId methodId)1147 void MatchAndMarkMethod(const CString &recordName, const char *methodName, EntityId methodId) 1148 { 1149 auto iter = methodIds_.find(recordName); 1150 if (iter != methodIds_.end()) { 1151 return iter->second->MatchAndMarkMethod(methodName, methodId); 1152 } 1153 } 1154 GetMismatchResult(uint32_t & totalMethodCount,uint32_t & mismatchMethodCount,std::set<std::pair<std::string,CString>> & mismatchMethodSet)1155 void GetMismatchResult(uint32_t &totalMethodCount, uint32_t &mismatchMethodCount, 1156 std::set<std::pair<std::string, CString>> &mismatchMethodSet) const 1157 { 1158 for (const auto &methodId : methodIds_) { 1159 methodId.second->GetMismatchResult(methodId.first, totalMethodCount, mismatchMethodCount, 1160 mismatchMethodSet); 1161 } 1162 } 1163 1164 void ParseFromBinary(void *buffer, PGOProfilerHeader *const header); 1165 1166 void Merge(const PGORecordSimpleInfos &simpleInfos); 1167 1168 NO_COPY_SEMANTIC(PGORecordSimpleInfos); 1169 NO_MOVE_SEMANTIC(PGORecordSimpleInfos); 1170 1171 private: 1172 bool ParseFromBinaryForLayout(void **buffer, PGOProfilerHeader *const header); 1173 1174 uint32_t hotnessThreshold_ {2}; 1175 NativeAreaAllocator nativeAreaAllocator_; 1176 std::unique_ptr<Chunk> chunk_; 1177 CUnorderedMap<CString, PGOMethodIdSet *> methodIds_; 1178 std::set<PGOHClassLayoutDesc> moduleLayoutDescInfos_; 1179 }; 1180 } // namespace panda::ecmascript 1181 #endif // ECMASCRIPT_PGO_PROFILER_INFO_H 1182