1/*
2 * Copyright (c) 2023-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#include "ecmascript/pgo_profiler/pgo_profiler_info.h"
17#include <cstdint>
18#include <fstream>
19#include <iomanip>
20#include <memory>
21#include <utility>
22#include "ecmascript/js_thread.h"
23#include "ecmascript/ohos/framework_helper.h"
24#include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
25#include "libpandafile/bytecode_instruction-inl.h"
26
27namespace panda::ecmascript::pgo {
28using StringHelper = base::StringHelper;
29void PGOPandaFileInfos::ParseFromBinary(void *buffer, SectionInfo *const info)
30{
31    void *addr = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + info->offset_);
32    for (uint32_t i = 0; i < info->number_; i++) {
33        fileInfos_.emplace(*base::ReadBufferInSize<FileInfo>(&addr));
34    }
35    LOG_ECMA(DEBUG) << "Profiler panda file count:" << info->number_;
36}
37
38void PGOPandaFileInfos::ProcessToBinary(std::fstream &fileStream, SectionInfo *info) const
39{
40    fileStream.seekp(info->offset_);
41    info->number_ = fileInfos_.size();
42    for (auto localInfo : fileInfos_) {
43        fileStream.write(reinterpret_cast<char *>(&localInfo), localInfo.Size());
44    }
45    info->size_ = static_cast<uint32_t>(fileStream.tellp()) - info->offset_;
46}
47
48void PGOPandaFileInfos::Merge(const PGOPandaFileInfos &pandaFileInfos)
49{
50    for (const auto &info : pandaFileInfos.fileInfos_) {
51        fileInfos_.emplace(info.GetChecksum());
52    }
53}
54
55bool PGOPandaFileInfos::VerifyChecksum(const PGOPandaFileInfos &pandaFileInfos, const std::string &base,
56                                       const std::string &incoming) const
57{
58    std::set<FileInfo> unionChecksum;
59    set_union(fileInfos_.begin(), fileInfos_.end(), pandaFileInfos.fileInfos_.begin(), pandaFileInfos.fileInfos_.end(),
60              inserter(unionChecksum, unionChecksum.begin()));
61    if (!fileInfos_.empty() && unionChecksum.empty()) {
62        LOG_ECMA(ERROR) << "First AP file(" << base << ") and the incoming file(" << incoming
63                        << ") do not come from the same abc file, skip merge the incoming file.";
64        return false;
65    }
66    return true;
67}
68
69bool PGOPandaFileInfos::ParseFromText(std::ifstream &stream)
70{
71    std::string pandaFileInfo;
72    while (std::getline(stream, pandaFileInfo)) {
73        if (pandaFileInfo.empty()) {
74            continue;
75        }
76
77        size_t start = pandaFileInfo.find_first_of(DumpUtils::ARRAY_START);
78        size_t end = pandaFileInfo.find_last_of(DumpUtils::ARRAY_END);
79        if (start == std::string::npos || end == std::string::npos || start >= end) {
80            return false;
81        }
82        auto content = pandaFileInfo.substr(start + 1, end - (start + 1) - 1);
83        std::vector<std::string> infos = StringHelper::SplitString(content, DumpUtils::BLOCK_SEPARATOR);
84        for (auto checksum : infos) {
85            uint32_t result;
86            if (!StringHelper::StrToUInt32(checksum.c_str(), &result)) {
87                LOG_ECMA(ERROR) << "checksum: " << checksum << " parse failed";
88                return false;
89            }
90            Sample(result);
91        }
92        return true;
93    }
94    return true;
95}
96
97void PGOPandaFileInfos::ProcessToText(std::ofstream &stream) const
98{
99    std::string pandaFileInfo = DumpUtils::NEW_LINE + DumpUtils::PANDA_FILE_INFO_HEADER;
100    bool isFirst = true;
101    for (auto &info : fileInfos_) {
102        if (!isFirst) {
103            pandaFileInfo += DumpUtils::BLOCK_SEPARATOR + DumpUtils::SPACE;
104        } else {
105            isFirst = false;
106        }
107        pandaFileInfo += std::to_string(info.GetChecksum());
108    }
109
110    pandaFileInfo += (DumpUtils::SPACE + DumpUtils::ARRAY_END + DumpUtils::NEW_LINE);
111    stream << pandaFileInfo;
112}
113
114bool PGOPandaFileInfos::Checksum(uint32_t checksum) const
115{
116    if (fileInfos_.find(checksum) == fileInfos_.end()) {
117        LOG_ECMA(ERROR) << "Checksum verification failed. Please ensure that the .abc and .ap match.";
118        return false;
119    }
120    return true;
121}
122
123void PGOMethodInfo::ProcessToText(std::string &text) const
124{
125    text += std::to_string(GetMethodId().GetOffset());
126    text += DumpUtils::ELEMENT_SEPARATOR;
127    text += std::to_string(GetCount());
128    text += DumpUtils::ELEMENT_SEPARATOR;
129    text += GetSampleModeToString();
130    text += DumpUtils::ELEMENT_SEPARATOR;
131    text += GetMethodName();
132}
133
134void PGOMethodInfo::ProcessToJson(ProfileType::VariantMap &function) const
135{
136    std::string methodName = GetMethodName();
137    std::string functionName = methodName + "(" + std::to_string(GetMethodId().GetOffset()) + ")";
138    function.insert(std::make_pair(DumpJsonUtils::FUNCTION_NAME, functionName));
139}
140
141std::vector<std::string> PGOMethodInfo::ParseFromText(const std::string &infoString)
142{
143    std::vector<std::string> infoStrings = StringHelper::SplitString(infoString, DumpUtils::ELEMENT_SEPARATOR);
144    return infoStrings;
145}
146
147uint32_t PGOMethodInfo::CalcChecksum(const char *name, const uint8_t *byteCodeArray, uint32_t byteCodeLength)
148{
149    uint32_t checksum = 0;
150    if (byteCodeArray != nullptr) {
151        checksum = CalcOpCodeChecksum(byteCodeArray, byteCodeLength);
152    }
153
154    if (name != nullptr) {
155        checksum = adler32(checksum, reinterpret_cast<const Bytef *>(name), strlen(name));
156    }
157    return checksum;
158}
159
160uint32_t PGOMethodInfo::CalcOpCodeChecksum(const uint8_t *byteCodeArray, uint32_t byteCodeLength)
161{
162    uint32_t checksum = 0;
163    BytecodeInstruction bcIns(byteCodeArray);
164    auto bcInsLast = bcIns.JumpTo(byteCodeLength);
165    while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
166        auto opCode = bcIns.GetOpcode();
167        checksum = adler32(checksum, reinterpret_cast<const Bytef *>(&opCode), sizeof(decltype(opCode)));
168        bcIns = bcIns.GetNext();
169    }
170    return checksum;
171}
172
173bool PGOMethodInfoMap::AddMethod(Chunk *chunk, Method *jsMethod, SampleMode mode)
174{
175    PGOMethodId methodId(jsMethod->GetMethodId());
176    auto result = methodInfos_.find(methodId);
177    if (result != methodInfos_.end()) {
178        auto info = result->second;
179        info->IncreaseCount();
180        info->SetSampleMode(mode);
181        return false;
182    } else {
183        CString methodName = jsMethod->GetMethodName();
184        size_t strlen = methodName.size();
185        size_t size = static_cast<size_t>(PGOMethodInfo::Size(strlen));
186        void *infoAddr = chunk->Allocate(size);
187        if (infoAddr == nullptr) {
188            LOG_ECMA(ERROR) << "infoAddr is null!";
189            return false;
190        }
191        auto info = new (infoAddr) PGOMethodInfo(methodId, 0, mode, methodName.c_str());
192        info->IncreaseCount();
193        methodInfos_.emplace(methodId, info);
194        auto checksum = PGOMethodInfo::CalcChecksum(jsMethod->GetMethodName(), jsMethod->GetBytecodeArray(),
195                                                    jsMethod->GetCodeSize());
196        methodsChecksum_.emplace(methodId, checksum);
197        return true;
198    }
199}
200
201PGOMethodTypeSet *PGOMethodInfoMap::GetOrInsertMethodTypeSet(Chunk *chunk, PGOMethodId methodId)
202{
203    auto typeInfoSetIter = methodTypeInfos_.find(methodId);
204    if (typeInfoSetIter != methodTypeInfos_.end()) {
205        return typeInfoSetIter->second;
206    } else {
207        auto typeInfoSet = chunk->New<PGOMethodTypeSet>();
208        methodTypeInfos_.emplace(methodId, typeInfoSet);
209        return typeInfoSet;
210    }
211}
212
213bool PGOMethodInfoMap::AddType(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type)
214{
215    auto typeInfoSet = GetOrInsertMethodTypeSet(chunk, methodId);
216    ASSERT(typeInfoSet != nullptr);
217    typeInfoSet->AddType(offset, type);
218    return true;
219}
220
221bool PGOMethodInfoMap::AddCallTargetType(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type)
222{
223    auto typeInfoSet = GetOrInsertMethodTypeSet(chunk, methodId);
224    ASSERT(typeInfoSet != nullptr);
225    typeInfoSet->AddCallTargetType(offset, type);
226    return true;
227}
228
229bool PGOMethodInfoMap::AddObjectInfo(Chunk *chunk, PGOMethodId methodId, int32_t offset, const PGOObjectInfo &info)
230{
231    auto typeInfoSet = GetOrInsertMethodTypeSet(chunk, methodId);
232    ASSERT(typeInfoSet != nullptr);
233    typeInfoSet->AddObjectInfo(offset, info);
234    return true;
235}
236
237bool PGOMethodInfoMap::AddDefine(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGODefineOpType type)
238{
239    auto typeInfoSet = GetOrInsertMethodTypeSet(chunk, methodId);
240    ASSERT(typeInfoSet != nullptr);
241    typeInfoSet->AddDefine(offset, type);
242    return true;
243}
244
245void PGOMethodInfoMap::Merge(Chunk *chunk, PGOMethodInfoMap *methodInfos)
246{
247    for (auto iter = methodInfos->methodInfos_.begin(); iter != methodInfos->methodInfos_.end(); iter++) {
248        auto methodId = iter->first;
249        auto fromMethodInfo = iter->second;
250
251        auto result = methodInfos_.find(methodId);
252        if (result != methodInfos_.end()) {
253            auto toMethodInfo = result->second;
254            toMethodInfo->Merge(fromMethodInfo);
255        } else {
256            size_t len = strlen(fromMethodInfo->GetMethodName());
257            size_t size = static_cast<size_t>(PGOMethodInfo::Size(len));
258            void *infoAddr = chunk->Allocate(size);
259            auto newMethodInfo = new (infoAddr) PGOMethodInfo(
260                methodId, fromMethodInfo->GetCount(), fromMethodInfo->GetSampleMode(), fromMethodInfo->GetMethodName());
261            methodInfos_.emplace(methodId, newMethodInfo);
262        }
263        fromMethodInfo->ClearCount();
264    }
265
266    for (auto iter = methodInfos->methodTypeInfos_.begin(); iter != methodInfos->methodTypeInfos_.end(); iter++) {
267        auto methodId = iter->first;
268        auto fromTypeInfo = iter->second;
269
270        auto result = methodTypeInfos_.find(methodId);
271        if (result != methodTypeInfos_.end()) {
272            auto toTypeInfo = result->second;
273            toTypeInfo->Merge(fromTypeInfo);
274        } else {
275            auto typeInfoSet = chunk->New<PGOMethodTypeSet>();
276            typeInfoSet->Merge(fromTypeInfo);
277            methodTypeInfos_.emplace(methodId, typeInfoSet);
278        }
279    }
280
281    for (auto iter = methodInfos->methodsChecksum_.begin(); iter != methodInfos->methodsChecksum_.end(); iter++) {
282        auto methodId = iter->first;
283        auto result = methodsChecksum_.find(methodId);
284        if (result == methodsChecksum_.end()) {
285            methodsChecksum_.emplace(methodId, iter->second);
286        }
287    }
288}
289
290bool PGOMethodInfoMap::ParseFromBinary(Chunk *chunk, PGOContext &context, void **buffer)
291{
292    PGOProfilerHeader *const header = context.GetHeader();
293    ASSERT(header != nullptr);
294    SectionInfo secInfo = base::ReadBuffer<SectionInfo>(buffer);
295    for (uint32_t j = 0; j < secInfo.number_; j++) {
296        PGOMethodInfo *info = base::ReadBufferInSize<PGOMethodInfo>(buffer);
297        if (info->IsFilter(context.GetHotnessThreshold())) {
298            if (header->SupportMethodChecksum()) {
299                base::ReadBuffer<uint32_t>(buffer, sizeof(uint32_t));
300            }
301            if (header->SupportType()) {
302                PGOMethodTypeSet::SkipFromBinary(buffer);
303            }
304            continue;
305        }
306        methodInfos_.emplace(info->GetMethodId(), info);
307        LOG_ECMA(DEBUG) << "Method:" << info->GetMethodId() << DumpUtils::ELEMENT_SEPARATOR << info->GetCount()
308                        << DumpUtils::ELEMENT_SEPARATOR << std::to_string(static_cast<int>(info->GetSampleMode()))
309                        << DumpUtils::ELEMENT_SEPARATOR << info->GetMethodName();
310        if (header->SupportMethodChecksum()) {
311            auto checksum = base::ReadBuffer<uint32_t>(buffer, sizeof(uint32_t));
312            methodsChecksum_.emplace(info->GetMethodId(), checksum);
313        }
314        if (header->SupportType()) {
315            auto typeInfoSet = chunk->New<PGOMethodTypeSet>();
316            typeInfoSet->ParseFromBinary(context, buffer);
317            methodTypeInfos_.emplace(info->GetMethodId(), typeInfoSet);
318        }
319    }
320    return !methodInfos_.empty();
321}
322
323bool PGOMethodInfoMap::ProcessToBinary(PGOContext &context, ProfileTypeRef recordProfileRef, const SaveTask *task,
324                                       std::fstream &stream, PGOProfilerHeader *const header) const
325{
326    SectionInfo secInfo;
327    std::stringstream methodStream;
328    for (auto iter = methodInfos_.begin(); iter != methodInfos_.end(); iter++) {
329        LOG_ECMA(DEBUG) << "Method:" << iter->first << DumpUtils::ELEMENT_SEPARATOR << iter->second->GetCount()
330                        << DumpUtils::ELEMENT_SEPARATOR
331                        << std::to_string(static_cast<int>(iter->second->GetSampleMode()))
332                        << DumpUtils::ELEMENT_SEPARATOR << iter->second->GetMethodName();
333        if (task && task->IsTerminate()) {
334            LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate";
335            return false;
336        }
337        auto curMethodInfo = iter->second;
338        if (curMethodInfo->IsFilter(context.GetHotnessThreshold())) {
339            continue;
340        }
341        methodStream.write(reinterpret_cast<char *>(curMethodInfo), curMethodInfo->Size());
342        if (header->SupportMethodChecksum()) {
343            auto checksumIter = methodsChecksum_.find(curMethodInfo->GetMethodId());
344            uint32_t checksum = 0;
345            if (checksumIter != methodsChecksum_.end()) {
346                checksum = checksumIter->second;
347            }
348            methodStream.write(reinterpret_cast<char *>(&checksum), sizeof(uint32_t));
349        }
350        if (header->SupportType()) {
351            auto typeInfoIter = methodTypeInfos_.find(curMethodInfo->GetMethodId());
352            if (typeInfoIter != methodTypeInfos_.end()) {
353                typeInfoIter->second->ProcessToBinary(context, methodStream);
354            } else {
355                uint32_t number = 0;
356                methodStream.write(reinterpret_cast<char *>(&number), sizeof(uint32_t));
357            }
358        }
359        secInfo.number_++;
360    }
361    if (secInfo.number_ > 0) {
362        secInfo.offset_ = sizeof(SectionInfo);
363        secInfo.size_ = static_cast<uint32_t>(methodStream.tellp());
364        stream.write(reinterpret_cast<char *>(&recordProfileRef), sizeof(uint32_t));
365        stream.write(reinterpret_cast<char *>(&secInfo), sizeof(SectionInfo));
366        stream << methodStream.rdbuf();
367        return true;
368    }
369    return false;
370}
371
372bool PGOMethodInfoMap::ParseFromText(Chunk *chunk, uint32_t threshold, const std::vector<std::string> &content)
373{
374    for (auto infoString : content) {
375        std::vector<std::string> infoStrings = PGOMethodInfo::ParseFromText(infoString);
376        if (infoStrings.size() < PGOMethodInfo::METHOD_INFO_COUNT) {
377            LOG_ECMA(ERROR) << "method info:" << infoString << " format error";
378            return false;
379        }
380        uint32_t count;
381        if (!StringHelper::StrToUInt32(infoStrings[PGOMethodInfo::METHOD_COUNT_INDEX].c_str(), &count)) {
382            LOG_ECMA(ERROR) << "count: " << infoStrings[PGOMethodInfo::METHOD_COUNT_INDEX] << " parse failed";
383            return false;
384        }
385        SampleMode mode;
386        if (!PGOMethodInfo::GetSampleMode(infoStrings[PGOMethodInfo::METHOD_MODE_INDEX], mode)) {
387            LOG_ECMA(ERROR) << "mode: " << infoStrings[PGOMethodInfo::METHOD_MODE_INDEX] << " parse failed";
388            return false;
389        }
390        if (count < threshold && mode == SampleMode::CALL_MODE) {
391            return true;
392        }
393        uint32_t methodId;
394        if (!StringHelper::StrToUInt32(infoStrings[PGOMethodInfo::METHOD_ID_INDEX].c_str(), &methodId)) {
395            LOG_ECMA(ERROR) << "method id: " << infoStrings[PGOMethodInfo::METHOD_ID_INDEX] << " parse failed";
396            return false;
397        }
398        std::string methodName = infoStrings[PGOMethodInfo::METHOD_NAME_INDEX];
399
400        void *infoAddr = chunk->Allocate(PGOMethodInfo::Size(methodName.size()));
401        auto info = new (infoAddr) PGOMethodInfo(PGOMethodId(methodId), count, mode, methodName.c_str());
402        methodInfos_.emplace(methodId, info);
403
404        // Parse Type Info
405        if (infoStrings.size() <= PGOMethodTypeSet::METHOD_TYPE_INFO_INDEX) {
406            continue;
407        }
408        std::string typeInfos = infoStrings[PGOMethodTypeSet::METHOD_TYPE_INFO_INDEX];
409        if (!typeInfos.empty()) {
410            size_t start = typeInfos.find_first_of(DumpUtils::ARRAY_START);
411            size_t end = typeInfos.find_last_of(DumpUtils::ARRAY_END);
412            if (start == std::string::npos || end == std::string::npos || start > end) {
413                LOG_ECMA(ERROR) << "Type info: " << typeInfos << " parse failed";
414                return false;
415            }
416            ASSERT(end > start + 1);
417            auto typeContent = typeInfos.substr(start + 1, end - (start + 1) - 1);
418            auto typeInfoSet = chunk->New<PGOMethodTypeSet>();
419            if (!typeInfoSet->ParseFromText(typeContent)) {
420                // delete by chunk
421                LOG_ECMA(ERROR) << "Type info: " << typeInfos << " parse failed";
422                return false;
423            }
424            methodTypeInfos_.emplace(info->GetMethodId(), typeInfoSet);
425        }
426    }
427
428    return true;
429}
430
431void PGOMethodInfoMap::ProcessToText(uint32_t threshold, const CString &recordName, std::ofstream &stream) const
432{
433    std::string profilerString;
434    bool isFirst = true;
435    for (auto methodInfoIter : methodInfos_) {
436        auto methodInfo = methodInfoIter.second;
437        if (methodInfo->IsFilter(threshold)) {
438            continue;
439        }
440        if (isFirst) {
441            profilerString += DumpUtils::NEW_LINE;
442            profilerString += recordName;
443            profilerString += DumpUtils::BLOCK_AND_ARRAY_START;
444            isFirst = false;
445        } else {
446            profilerString += DumpUtils::BLOCK_SEPARATOR + DumpUtils::SPACE;
447        }
448        methodInfo->ProcessToText(profilerString);
449        profilerString += DumpUtils::ELEMENT_SEPARATOR;
450        auto checksumIter = methodsChecksum_.find(methodInfo->GetMethodId());
451        if (checksumIter != methodsChecksum_.end()) {
452            std::stringstream parseStream;
453            parseStream << std::internal << std::setfill('0') << std::showbase
454                        << std::setw(DumpUtils::HEX_FORMAT_WIDTH_FOR_32BITS) << std::hex << checksumIter->second
455                        << DumpUtils::ELEMENT_SEPARATOR;
456            profilerString += parseStream.str();
457        }
458        auto iter = methodTypeInfos_.find(methodInfo->GetMethodId());
459        if (iter != methodTypeInfos_.end()) {
460            iter->second->ProcessToText(profilerString);
461        }
462    }
463    if (!isFirst) {
464        profilerString += (DumpUtils::SPACE + DumpUtils::ARRAY_END + DumpUtils::NEW_LINE);
465        stream << profilerString;
466    }
467}
468
469void PGOMethodInfoMap::ProcessToJson(uint32_t threshold, ProfileType::jModuleType &jModule) const
470{
471    std::vector<ProfileType::VariantMap> functionArray;
472    for (auto methodInfoIter : methodInfos_) {
473        auto methodInfo = methodInfoIter.second;
474        if (methodInfo->IsFilter(threshold)) {
475            continue;
476        }
477        ProfileType::VariantMap function;
478        methodInfo->ProcessToJson(function);
479        auto iter = methodTypeInfos_.find(methodInfo->GetMethodId());
480        if (iter != methodTypeInfos_.end()) {
481            ProfileType::VariantVector typeArray;
482            iter->second->ProcessToJson(typeArray);
483            function.insert(std::make_pair(DumpJsonUtils::TYPE, typeArray));
484        }
485        functionArray.push_back(function);
486    }
487    jModule.insert(std::make_pair(DumpJsonUtils::FUNCTION, functionArray));
488}
489
490bool PGOMethodIdSet::ParseFromBinary(PGOContext &context, void **buffer)
491{
492    PGOProfilerHeader *const header = context.GetHeader();
493    ASSERT(header != nullptr);
494    SectionInfo secInfo = base::ReadBuffer<SectionInfo>(buffer);
495    for (uint32_t j = 0; j < secInfo.number_; j++) {
496        PGOMethodInfo *info = base::ReadBufferInSize<PGOMethodInfo>(buffer);
497        if (info->IsFilter(context.GetHotnessThreshold())) {
498            if (header->SupportMethodChecksum()) {
499                base::ReadBuffer<uint32_t>(buffer, sizeof(uint32_t));
500            }
501            if (header->SupportType()) {
502                PGOMethodTypeSet::SkipFromBinary(buffer);
503            }
504            continue;
505        }
506        uint32_t checksum = 0;
507        if (header->SupportMethodChecksum()) {
508            checksum = base::ReadBuffer<uint32_t>(buffer, sizeof(uint32_t));
509        }
510        auto ret = methodInfoMap_.try_emplace(info->GetMethodName(), chunk_);
511        auto methodNameSetIter = ret.first;
512        auto &methodInfo = methodNameSetIter->second.GetOrCreateMethodInfo(checksum, info->GetMethodId());
513        LOG_ECMA(DEBUG) << "Method:" << info->GetMethodId() << DumpUtils::ELEMENT_SEPARATOR << info->GetCount()
514                        << DumpUtils::ELEMENT_SEPARATOR << std::to_string(static_cast<int>(info->GetSampleMode()))
515                        << DumpUtils::ELEMENT_SEPARATOR << info->GetMethodName();
516        if (header->SupportType()) {
517            methodInfo.GetPGOMethodTypeSet().ParseFromBinary(context, buffer);
518        }
519    }
520
521    return !methodInfoMap_.empty();
522}
523
524void PGOMethodIdSet::GetMismatchResult(const CString &recordName, uint32_t &totalMethodCount,
525                                       uint32_t &mismatchMethodCount,
526                                       std::set<std::pair<std::string, CString>> &mismatchMethodSet) const
527{
528    totalMethodCount += methodInfoMap_.size();
529    for (const auto &methodNameSet : methodInfoMap_) {
530        if (methodNameSet.second.IsMatch()) {
531            continue;
532        }
533        auto info = std::make_pair(methodNameSet.first, recordName);
534        mismatchMethodSet.emplace(info);
535        mismatchMethodCount++;
536    }
537}
538
539void PGOMethodIdSet::Merge(const PGOMethodIdSet &from)
540{
541    for (const auto &methodNameSet : from.methodInfoMap_) {
542        auto iter = methodInfoMap_.find(methodNameSet.first);
543        if (iter == methodInfoMap_.end()) {
544            auto ret = methodInfoMap_.try_emplace(methodNameSet.first, chunk_);
545            iter = ret.first;
546        }
547        const_cast<PGOMethodNameSet &>(iter->second).Merge(methodNameSet.second);
548    }
549}
550
551void PGODecodeMethodInfo::Merge(const PGODecodeMethodInfo &from)
552{
553    ASSERT(methodId_.IsValid() && from.methodId_.IsValid());
554    if (!(methodId_ == from.methodId_)) {
555        LOG_ECMA(ERROR) << "MethodId not match. " << methodId_ << " vs " << from.methodId_;
556        return;
557    }
558    pgoMethodTypeSet_.Merge(&from.pgoMethodTypeSet_);
559}
560
561PGORecordDetailInfos::PGORecordDetailInfos(uint32_t hotnessThreshold) : hotnessThreshold_(hotnessThreshold)
562{
563    chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_);
564    InitSections();
565};
566
567PGORecordDetailInfos::~PGORecordDetailInfos()
568{
569    Clear();
570}
571
572PGOMethodInfoMap *PGORecordDetailInfos::GetMethodInfoMap(ProfileType recordProfileType)
573{
574    auto iter = recordInfos_.find(recordProfileType);
575    if (iter != recordInfos_.end()) {
576        return iter->second;
577    } else {
578        auto curMethodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
579        recordInfos_.emplace(recordProfileType, curMethodInfos);
580        return curMethodInfos;
581    }
582}
583
584bool PGORecordDetailInfos::AddMethod(ProfileType recordProfileType, Method *jsMethod, SampleMode mode)
585{
586    auto curMethodInfos = GetMethodInfoMap(recordProfileType);
587    ASSERT(curMethodInfos != nullptr);
588    ASSERT(jsMethod != nullptr);
589    return curMethodInfos->AddMethod(chunk_.get(), jsMethod, mode);
590}
591
592bool PGORecordDetailInfos::AddType(ProfileType recordProfileType, PGOMethodId methodId, int32_t offset,
593                                   PGOSampleType type)
594{
595    auto curMethodInfos = GetMethodInfoMap(recordProfileType);
596    ASSERT(curMethodInfos != nullptr);
597    return curMethodInfos->AddType(chunk_.get(), methodId, offset, type);
598}
599
600bool PGORecordDetailInfos::AddCallTargetType(ProfileType recordProfileType, PGOMethodId methodId, int32_t offset,
601                                             PGOSampleType type)
602{
603    auto curMethodInfos = GetMethodInfoMap(recordProfileType);
604    ASSERT(curMethodInfos != nullptr);
605    return curMethodInfos->AddCallTargetType(chunk_.get(), methodId, offset, type);
606}
607
608bool PGORecordDetailInfos::AddObjectInfo(
609    ProfileType recordProfileType, EntityId methodId, int32_t offset, const PGOObjectInfo &info)
610{
611    auto curMethodInfos = GetMethodInfoMap(recordProfileType);
612    ASSERT(curMethodInfos != nullptr);
613    return curMethodInfos->AddObjectInfo(chunk_.get(), methodId, offset, info);
614}
615
616bool PGORecordDetailInfos::AddDefine(
617    ProfileType recordProfileType, PGOMethodId methodId, int32_t offset, PGODefineOpType type)
618{
619    auto curMethodInfos = GetMethodInfoMap(recordProfileType);
620    ASSERT(curMethodInfos != nullptr);
621    curMethodInfos->AddDefine(chunk_.get(), methodId, offset, type);
622
623    PGOHClassTreeDesc descInfo(type.GetProfileType());
624    auto iter = hclassTreeDescInfos_.find(descInfo);
625    if (iter == hclassTreeDescInfos_.end()) {
626        descInfo.SetProtoPt(type.GetProtoTypePt());
627        hclassTreeDescInfos_.emplace(descInfo);
628    } else {
629        const_cast<PGOHClassTreeDesc &>(*iter).SetProtoPt(type.GetProtoTypePt());
630    }
631    return true;
632}
633
634bool PGORecordDetailInfos::AddRootLayout(JSTaggedType hclass, ProfileType rootType)
635{
636    PGOHClassTreeDesc descInfo(rootType);
637    auto iter = hclassTreeDescInfos_.find(descInfo);
638    if (iter != hclassTreeDescInfos_.end()) {
639        return const_cast<PGOHClassTreeDesc &>(*iter).DumpForRoot(hclass, rootType);
640    } else {
641        if (!descInfo.DumpForRoot(hclass, rootType)) {
642            return false;
643        }
644        hclassTreeDescInfos_.emplace(descInfo);
645    }
646    return true;
647}
648
649bool PGORecordDetailInfos::UpdateLayout(ProfileType rootType, JSTaggedType hclass, ProfileType curType)
650{
651    PGOHClassTreeDesc descInfo(rootType);
652    auto iter = hclassTreeDescInfos_.find(descInfo);
653    if (iter != hclassTreeDescInfos_.end()) {
654        return const_cast<PGOHClassTreeDesc &>(*iter).UpdateLayout(hclass, curType);
655    } else {
656        if (!descInfo.UpdateLayout(hclass, curType)) {
657            return false;
658        }
659        hclassTreeDescInfos_.emplace(descInfo);
660        return false;
661    }
662    return true;
663}
664
665bool PGORecordDetailInfos::UpdateTransitionLayout(
666    ProfileType rootType, JSTaggedType parent, ProfileType parentType, JSTaggedType child, ProfileType childType)
667{
668    PGOHClassTreeDesc descInfo(rootType);
669    auto iter = hclassTreeDescInfos_.find(descInfo);
670    if (iter != hclassTreeDescInfos_.end()) {
671        return const_cast<PGOHClassTreeDesc &>(*iter).UpdateForTransition(parent, parentType, child, childType);
672    } else {
673        if (!descInfo.UpdateForTransition(parent, parentType, child, childType)) {
674            return false;
675        }
676        hclassTreeDescInfos_.emplace(descInfo);
677    }
678    return true;
679}
680
681void PGORecordDetailInfos::AddRootPtType(ProfileType rootType, ProfileType ptType)
682{
683    PGOHClassTreeDesc descInfo(rootType);
684    auto iter = hclassTreeDescInfos_.find(descInfo);
685    if (iter != hclassTreeDescInfos_.end()) {
686        const_cast<PGOHClassTreeDesc &>(*iter).SetProtoPt(ptType);
687    } else {
688        descInfo.SetProtoPt(ptType);
689        hclassTreeDescInfos_.emplace(descInfo);
690    }
691}
692
693bool PGORecordDetailInfos::IsDumped(ProfileType rootType, ProfileType curType) const
694{
695    PGOHClassTreeDesc descInfo(rootType);
696    auto iter = hclassTreeDescInfos_.find(descInfo);
697    if (iter != hclassTreeDescInfos_.end()) {
698        return const_cast<PGOHClassTreeDesc &>(*iter).IsDumped(curType);
699    }
700    return false;
701}
702
703void PGORecordDetailInfos::Merge(const PGORecordDetailInfos &recordInfos)
704{
705    CMap<ProfileType, PGOMethodInfoMap *> methodInfos = recordInfos.recordInfos_;
706    for (auto iter = methodInfos.begin(); iter != methodInfos.end(); iter++) {
707        auto recordType = iter->first;
708        auto fromMethodInfos = iter->second;
709
710        auto recordInfosIter = recordInfos_.find(recordType);
711        PGOMethodInfoMap *toMethodInfos = nullptr;
712        if (recordInfosIter == recordInfos_.end()) {
713            toMethodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
714            recordInfos_.emplace(recordType, toMethodInfos);
715        } else {
716            toMethodInfos = recordInfosIter->second;
717        }
718
719        ASSERT(toMethodInfos != nullptr);
720        toMethodInfos->Merge(chunk_.get(), fromMethodInfos);
721    }
722
723    recordPool_->Merge(*recordInfos.recordPool_);
724    protoTransitionPool_->Merge(*recordInfos.protoTransitionPool_);
725    // Merge global layout desc infos to global method info map
726    std::set<PGOHClassTreeDesc> hclassTreeDescInfos = recordInfos.hclassTreeDescInfos_;
727    for (auto info = hclassTreeDescInfos.begin(); info != hclassTreeDescInfos.end();
728         info++) {
729        auto &fromInfo = *info;
730        auto result = hclassTreeDescInfos_.find(fromInfo);
731        if (result == hclassTreeDescInfos_.end()) {
732            PGOHClassTreeDesc descInfo(fromInfo.GetProfileType());
733            descInfo.SetProtoPt(fromInfo.GetProtoPt());
734            descInfo.Merge(fromInfo);
735            hclassTreeDescInfos_.emplace(descInfo);
736        } else {
737            const_cast<PGOHClassTreeDesc &>(*result).Merge(fromInfo);
738        }
739    }
740}
741
742bool PGORecordDetailInfos::ParseFromBinary(void *buffer, PGOProfilerHeader *const header)
743{
744    header_ = header;
745    // ProfileTypePool must be parsed at first
746    PGOFileSectionInterface::ParseSectionFromBinary(*this, buffer, header, *profileTypePool_->GetPool());
747    if (!abcIdRemap_.empty()) {
748        // step2: [abc pool merge] remap decoder's profileType pool's abcId field.
749        LOG_ECMA(DEBUG) << "remap with abcRemapSize: " << abcIdRemap_.size();
750        profileTypePool_->Remap(*this);
751    }
752    PGOFileSectionInterface::ParseSectionFromBinary(*this, buffer, header, *protoTransitionPool_);
753    PGOFileSectionInterface::ParseSectionFromBinary(*this, buffer, header, *recordPool_);
754    SectionInfo *info = header->GetRecordInfoSection();
755    void *addr = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + info->offset_);
756    for (uint32_t i = 0; i < info->number_; i++) {
757        ApEntityId recordId(0);
758        ProfileType recordType;
759        if (header->SupportProfileTypeWithAbcId()) {
760            auto recordTypeRef = ProfileTypeRef(base::ReadBuffer<ApEntityId>(&addr, sizeof(ApEntityId)));
761            auto res = ProfileType::CreateFromProfileTypeRef(*this, recordTypeRef);
762            if (!res.has_value()) {
763                LOG_ECMA(ERROR) << "ParseFromBinary failed, current addr: " << addr << std::endl;
764                return false;
765            }
766            recordType = res.value();
767            recordId = recordType.GetId();
768        } else if (header->SupportRecordPool()) {
769            recordId = base::ReadBuffer<ApEntityId>(&addr, sizeof(ApEntityId));
770        } else {
771            auto *recordName = base::ReadBuffer(&addr);
772            recordPool_->Add(ProfileType(recordId), recordName);
773        }
774        recordType.UpdateId(recordId);
775        recordType.UpdateKind(ProfileType::Kind::RecordClassId);
776        PGOMethodInfoMap *methodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
777        ASSERT(methodInfos != nullptr);
778        if (methodInfos->ParseFromBinary(chunk_.get(), *this, &addr)) {
779            recordInfos_.emplace(recordType, methodInfos);
780        }
781    }
782
783    info = header->GetLayoutDescSection();
784    if (info == nullptr) {
785        return false;
786    }
787    if (header->SupportTrackField()) {
788        ParseFromBinaryForLayout(&addr);
789    }
790    return true;
791}
792
793bool PGORecordDetailInfos::ParseFromBinaryForLayout(void **buffer)
794{
795    SectionInfo secInfo = base::ReadBuffer<SectionInfo>(buffer);
796    for (uint32_t i = 0; i < secInfo.number_; i++) {
797        auto *info = base::ReadBufferInSize<PGOHClassTreeDescInnerRef>(buffer);
798        if (info == nullptr) {
799            LOG_ECMA(INFO) << "Binary format error!";
800            continue;
801        }
802        hclassTreeDescInfos_.emplace(info->Convert(*this));
803    }
804    return true;
805}
806
807void PGORecordDetailInfos::ProcessToBinary(
808    const SaveTask *task, std::fstream &fileStream, PGOProfilerHeader *const header)
809{
810    header_ = header;
811    auto info = header->GetRecordInfoSection();
812    info->number_ = 0;
813    info->offset_ = static_cast<uint32_t>(fileStream.tellp());
814    for (auto iter = recordInfos_.begin(); iter != recordInfos_.end(); iter++) {
815        if (task && task->IsTerminate()) {
816            LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate";
817            break;
818        }
819        auto recordId = iter->first;
820        auto curMethodInfos = iter->second;
821        if (curMethodInfos->ProcessToBinary(*this, ProfileTypeRef(*this, recordId), task, fileStream, header)) {
822            info->number_++;
823        }
824    }
825    info->size_ = static_cast<uint32_t>(fileStream.tellp()) - info->offset_;
826
827    info = header->GetLayoutDescSection();
828    if (info == nullptr) {
829        return;
830    }
831    info->number_ = 0;
832    info->offset_ = static_cast<uint32_t>(fileStream.tellp());
833    if (header->SupportType()) {
834        if (!ProcessToBinaryForLayout(const_cast<NativeAreaAllocator *>(&nativeAreaAllocator_), task, fileStream)) {
835            return;
836        }
837        info->number_++;
838    }
839    info->size_ = static_cast<uint32_t>(fileStream.tellp()) - info->offset_;
840
841    PGOFileSectionInterface::ProcessSectionToBinary(*this, fileStream, header, *recordPool_);
842    PGOFileSectionInterface::ProcessSectionToBinary(*this, fileStream, header, *protoTransitionPool_);
843    // ProfileTypePool must be processed at last
844    PGOFileSectionInterface::ProcessSectionToBinary(*this, fileStream, header, *profileTypePool_->GetPool());
845}
846
847bool PGORecordDetailInfos::ProcessToBinaryForLayout(
848    NativeAreaAllocator *allocator, const SaveTask *task, std::fstream &stream)
849{
850    SectionInfo secInfo;
851    auto layoutBeginPosition = stream.tellp();
852    stream.seekp(sizeof(SectionInfo), std::ofstream::cur);
853    for (const auto &typeInfo : hclassTreeDescInfos_) {
854        if (task && task->IsTerminate()) {
855            LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate";
856            return false;
857        }
858        auto profileType = PGOSampleType(typeInfo.GetProfileType());
859        size_t size = PGOHClassTreeDescInnerRef::CaculateSize(typeInfo);
860        if (size == 0) {
861            continue;
862        }
863
864        PGOSampleTypeRef classRef = PGOSampleTypeRef::ConvertFrom(*this, profileType);
865        auto protoSt = PGOSampleType(typeInfo.GetProtoPt());
866        PGOSampleTypeRef protoClassRef = PGOSampleTypeRef::ConvertFrom(*this, protoSt);
867        void *addr = allocator->Allocate(size);
868        auto descInfos = new (addr) PGOHClassTreeDescInnerRef(size, classRef, protoClassRef);
869        descInfos->Merge(typeInfo);
870        stream.write(reinterpret_cast<char *>(descInfos), size);
871        allocator->Delete(addr);
872        secInfo.number_++;
873    }
874
875    secInfo.offset_ = sizeof(SectionInfo);
876    secInfo.size_ = static_cast<uint32_t>(stream.tellp()) -
877        static_cast<uint32_t>(layoutBeginPosition) - sizeof(SectionInfo);
878    stream.seekp(layoutBeginPosition, std::ofstream::beg)
879        .write(reinterpret_cast<char *>(&secInfo), sizeof(SectionInfo))
880        .seekp(0, std::ofstream::end);
881    return true;
882}
883
884bool PGORecordDetailInfos::ParseFromText(std::ifstream &stream)
885{
886    std::string details;
887    while (std::getline(stream, details)) {
888        if (details.empty()) {
889            continue;
890        }
891        size_t blockIndex = details.find(DumpUtils::BLOCK_AND_ARRAY_START);
892        if (blockIndex == std::string::npos) {
893            return false;
894        }
895        CString recordName = ConvertToString(details.substr(0, blockIndex));
896
897        size_t start = details.find_first_of(DumpUtils::ARRAY_START);
898        size_t end = details.find_last_of(DumpUtils::ARRAY_END);
899        if (start == std::string::npos || end == std::string::npos || start > end) {
900            return false;
901        }
902        ASSERT(end > start + 1);
903        auto content = details.substr(start + 1, end - (start + 1) - 1);
904        std::vector<std::string> infoStrings = StringHelper::SplitString(content, DumpUtils::BLOCK_SEPARATOR);
905        if (infoStrings.size() <= 0) {
906            continue;
907        }
908
909        ApEntityId recordId(0);
910        ProfileType profileType(0, recordId, ProfileType::Kind::RecordClassId);
911        auto methodInfosIter = recordInfos_.find(profileType);
912        PGOMethodInfoMap *methodInfos = nullptr;
913        if (methodInfosIter == recordInfos_.end()) {
914            methodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
915            recordInfos_.emplace(profileType, methodInfos);
916        } else {
917            methodInfos = methodInfosIter->second;
918        }
919        ASSERT(methodInfos != nullptr);
920        if (!methodInfos->ParseFromText(chunk_.get(), hotnessThreshold_, infoStrings)) {
921            return false;
922        }
923    }
924    return true;
925}
926
927void PGORecordDetailInfos::ProcessToText(std::ofstream &stream) const
928{
929    std::string profilerString;
930    bool isFirst = true;
931    for (auto layoutInfoIter : hclassTreeDescInfos_) {
932        if (isFirst) {
933            profilerString += DumpUtils::NEW_LINE;
934            profilerString += DumpUtils::ARRAY_START + DumpUtils::SPACE;
935            isFirst = false;
936        } else {
937            profilerString += DumpUtils::BLOCK_SEPARATOR + DumpUtils::SPACE;
938        }
939        profilerString += PGOHClassTreeDescInner::GetTypeString(layoutInfoIter);
940    }
941    if (!isFirst) {
942        profilerString += (DumpUtils::SPACE + DumpUtils::ARRAY_END + DumpUtils::NEW_LINE);
943        stream << profilerString;
944    }
945    for (auto iter = recordInfos_.begin(); iter != recordInfos_.end(); iter++) {
946        const CString recordName(recordPool_->GetName(iter->first));
947        if (recordName.empty()) {
948            LOG_ECMA(ERROR) << "record name is empty, " << iter->first.GetTypeString();
949            continue;
950        }
951        auto methodInfos = iter->second;
952        methodInfos->ProcessToText(hotnessThreshold_, recordName, stream);
953    }
954    recordPool_->ProcessToText(stream);
955    protoTransitionPool_->ProcessToText(stream);
956    // ProfileTypePool must be processed at last
957    profileTypePool_->GetPool()->ProcessToText(stream);
958}
959
960void PGORecordDetailInfos::InitSections()
961{
962    recordPool_ = std::make_unique<PGORecordPool>();
963    protoTransitionPool_ = std::make_unique<PGOProtoTransitionPool>();
964    profileTypePool_ = std::make_unique<PGOProfileTypePool>();
965}
966
967void PGORecordDetailInfos::Clear()
968{
969    for (auto iter : recordInfos_) {
970        iter.second->Clear();
971        nativeAreaAllocator_.Delete(iter.second);
972    }
973    for (auto iter : hclassTreeDescInfos_) {
974        iter.Clear();
975    }
976    hclassTreeDescInfos_.clear();
977    recordInfos_.clear();
978    recordPool_->Clear();
979    protoTransitionPool_->Clear();
980    profileTypePool_->Clear();
981    hclassTreeDescInfos_.clear();
982    abcIdRemap_.clear();
983    chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_);
984    InitSections();
985}
986
987bool PGORecordSimpleInfos::Match(const CString &abcNormalizedDesc, const CString &recordName, EntityId methodId)
988{
989    auto abcMethodIds = methodIds_.find(abcNormalizedDesc);
990    if (abcMethodIds == methodIds_.end()) {
991        LOG_COMPILER(DEBUG) << "AbcDesc not found. abcNormalizedDesc: " << abcNormalizedDesc
992                            << ", methodIdsCount: " << methodIds_.size();
993        return false;
994    }
995    auto methodIdsIter = abcMethodIds->second.find(recordName);
996    if (methodIdsIter == abcMethodIds->second.end()) {
997        LOG_COMPILER(DEBUG) << "AbcDesc not found. recordName: " << recordName;
998        return false;
999    }
1000    return methodIdsIter->second->Match(methodId);
1001}
1002
1003void PGORecordSimpleInfos::ParseFromBinary(void *buffer, PGOProfilerHeader *const header,
1004                                           std::shared_ptr<PGOAbcFilePool> &abcFilePool)
1005{
1006    header_ = header;
1007    // ProfileTypePool must be parsed at first
1008    if (!PGOFileSectionInterface::ParseSectionFromBinary(*this, buffer, header, *profileTypePool_->GetPool())) {
1009        LOG_ECMA(ERROR) << "Parse from binary failed for profile type pool.";
1010        return;
1011    }
1012    if (!abcIdRemap_.empty()) {
1013        // step2: [abc pool merge] remap decoder's profileType pool's abcId field.
1014        LOG_ECMA(DEBUG) << "remap with abcRemapSize: " << abcIdRemap_.size();
1015        profileTypePool_->Remap(*this);
1016    }
1017    if (!PGOFileSectionInterface::ParseSectionFromBinary(*this, buffer, header, *protoTransitionPool_)) {
1018        LOG_ECMA(ERROR) << "Parse from binary failed for proto transition pool.";
1019        return;
1020    }
1021    if (!PGOFileSectionInterface::ParseSectionFromBinary(*this, buffer, header, *recordPool_)) {
1022        LOG_ECMA(ERROR) << "Parse from binary failed for record pool.";
1023        return;
1024    }
1025    SectionInfo *info = header->GetRecordInfoSection();
1026    void *addr = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + info->offset_);
1027    for (uint32_t i = 0; i < info->number_; i++) {
1028        CString recordName;
1029        const char *abcDesc = "";
1030        ProfileType recordType;
1031        if (header->SupportProfileTypeWithAbcId()) {
1032            auto recordTypeRef = ProfileTypeRef(base::ReadBuffer<ApEntityId>(&addr, sizeof(ApEntityId)));
1033            recordType = ProfileType(*this, recordTypeRef);
1034            recordName = recordPool_->GetName(recordType);
1035            auto abcId = recordType.GetAbcId();
1036            const auto *entry = abcFilePool->GetPool()->GetEntry(abcId);
1037            if (entry != nullptr) {
1038                abcDesc = entry->GetData().c_str();
1039            }
1040        } else if (header->SupportRecordPool()) {
1041            auto recordId = base::ReadBuffer<ApEntityId>(&addr, sizeof(ApEntityId));
1042            recordName = recordPool_->GetName(ProfileType(recordId));
1043        } else {
1044            recordName = base::ReadBuffer(&addr);
1045        }
1046        PGOMethodIdSet *methodIds = nativeAreaAllocator_.New<PGOMethodIdSet>(chunk_.get());
1047        if (methodIds->ParseFromBinary(*this, &addr)) {
1048            auto methodIdsResult = methodIds_.try_emplace(JSPandaFile::GetNormalizedFileDesc(abcDesc));
1049            // check record name, the default record name of the framework abc does not enter the aot compilation
1050            FrameworkHelper::GetRealRecordName(recordName);
1051            (methodIdsResult.first->second).emplace(recordName, methodIds);
1052        }
1053    }
1054
1055    info = header->GetLayoutDescSection();
1056    if (info == nullptr) {
1057        return;
1058    }
1059    if (header->SupportTrackField()) {
1060        ParseFromBinaryForLayout(&addr);
1061    }
1062}
1063
1064void PGORecordSimpleInfos::Merge(const PGORecordSimpleInfos &simpleInfos)
1065{
1066    for (const auto &fromAbcMethodIds : simpleInfos.methodIds_) {
1067        auto toAbcMethodIds = methodIds_.try_emplace(fromAbcMethodIds.first);
1068        for (const auto &method : fromAbcMethodIds.second) {
1069            auto result = toAbcMethodIds.first->second.find(method.first);
1070            if (result == toAbcMethodIds.first->second.end()) {
1071                PGOMethodIdSet *methodIds = nativeAreaAllocator_.New<PGOMethodIdSet>(chunk_.get());
1072                auto ret = toAbcMethodIds.first->second.emplace(method.first, methodIds);
1073                ASSERT(ret.second);
1074                result = ret.first;
1075            }
1076            const_cast<PGOMethodIdSet &>(*result->second).Merge(*method.second);
1077        }
1078    }
1079    recordPool_->Merge(*simpleInfos.recordPool_);
1080    protoTransitionPool_->Merge(*simpleInfos.protoTransitionPool_);
1081    // Merge global layout desc infos to global method info map
1082    for (const auto &hclassTreeDescInfo : simpleInfos.hclassTreeDescInfos_) {
1083        auto result = hclassTreeDescInfos_.find(hclassTreeDescInfo);
1084        if (result == hclassTreeDescInfos_.end()) {
1085            PGOHClassTreeDesc descInfo(hclassTreeDescInfo.GetProfileType());
1086            descInfo.SetProtoPt(hclassTreeDescInfo.GetProtoPt());
1087            descInfo.Merge(hclassTreeDescInfo);
1088            hclassTreeDescInfos_.emplace(descInfo);
1089        } else {
1090            const_cast<PGOHClassTreeDesc &>(*result).Merge(hclassTreeDescInfo);
1091        }
1092    }
1093}
1094
1095bool PGORecordSimpleInfos::ParseFromBinaryForLayout(void **buffer)
1096{
1097    SectionInfo secInfo = base::ReadBuffer<SectionInfo>(buffer);
1098    for (uint32_t i = 0; i < secInfo.number_; i++) {
1099        auto *info = base::ReadBufferInSize<PGOHClassTreeDescInnerRef>(buffer);
1100        if (info == nullptr) {
1101            LOG_ECMA(INFO) << "Binary format error!";
1102            continue;
1103        }
1104        hclassTreeDescInfos_.emplace(info->Convert(*this));
1105    }
1106    return true;
1107}
1108
1109void PGORecordSimpleInfos::InitSections()
1110{
1111    recordPool_ = std::make_unique<PGORecordPool>();
1112    protoTransitionPool_ = std::make_unique<PGOProtoTransitionPool>();
1113    profileTypePool_ = std::make_unique<PGOProfileTypePool>();
1114}
1115
1116void PGORecordSimpleInfos::Clear()
1117{
1118    for (const auto &abcMethodIds: methodIds_) {
1119        for (const auto &iter : abcMethodIds.second) {
1120            iter.second->Clear();
1121            nativeAreaAllocator_.Delete(iter.second);
1122        }
1123    }
1124    for (auto iter : hclassTreeDescInfos_) {
1125        iter.Clear();
1126    }
1127    hclassTreeDescInfos_.clear();
1128    methodIds_.clear();
1129    recordPool_->Clear();
1130    profileTypePool_->Clear();
1131    hclassTreeDescInfos_.clear();
1132    abcIdRemap_.clear();
1133    chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_);
1134    InitSections();
1135}
1136
1137PGORecordSimpleInfos::PGORecordSimpleInfos(uint32_t threshold) : hotnessThreshold_(threshold)
1138{
1139    chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_);
1140    InitSections();
1141}
1142
1143PGORecordSimpleInfos::~PGORecordSimpleInfos()
1144{
1145    Clear();
1146}
1147} // namespace panda::ecmascript::pgo
1148