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 
27 namespace panda::ecmascript::pgo {
28 using StringHelper = base::StringHelper;
ParseFromBinary(void *buffer, SectionInfo *const info)29 void 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 
ProcessToBinary(std::fstream &fileStream, SectionInfo *info) const38 void 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 
Merge(const PGOPandaFileInfos &pandaFileInfos)48 void PGOPandaFileInfos::Merge(const PGOPandaFileInfos &pandaFileInfos)
49 {
50     for (const auto &info : pandaFileInfos.fileInfos_) {
51         fileInfos_.emplace(info.GetChecksum());
52     }
53 }
54 
VerifyChecksum(const PGOPandaFileInfos &pandaFileInfos, const std::string &base, const std::string &incoming) const55 bool 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 
ParseFromText(std::ifstream &stream)69 bool 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 
ProcessToText(std::ofstream &stream) const97 void 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 
Checksum(uint32_t checksum) const114 bool 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 
ProcessToText(std::string &text) const123 void 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 
ProcessToJson(ProfileType::VariantMap &function) const134 void 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 
ParseFromText(const std::string &infoString)141 std::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 
CalcChecksum(const char *name, const uint8_t *byteCodeArray, uint32_t byteCodeLength)147 uint32_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 
CalcOpCodeChecksum(const uint8_t *byteCodeArray, uint32_t byteCodeLength)160 uint32_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 
AddMethod(Chunk *chunk, Method *jsMethod, SampleMode mode)173 bool 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 
GetOrInsertMethodTypeSet(Chunk *chunk, PGOMethodId methodId)201 PGOMethodTypeSet *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 
AddType(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type)213 bool 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 
AddCallTargetType(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type)221 bool 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 
AddObjectInfo(Chunk *chunk, PGOMethodId methodId, int32_t offset, const PGOObjectInfo &info)229 bool 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 
AddDefine(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGODefineOpType type)237 bool 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 
Merge(Chunk *chunk, PGOMethodInfoMap *methodInfos)245 void 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 
ParseFromBinary(Chunk *chunk, PGOContext &context, void **buffer)290 bool 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 
ProcessToBinary(PGOContext &context, ProfileTypeRef recordProfileRef, const SaveTask *task, std::fstream &stream, PGOProfilerHeader *const header) const323 bool 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 
ParseFromText(Chunk *chunk, uint32_t threshold, const std::vector<std::string> &content)372 bool 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 
ProcessToText(uint32_t threshold, const CString &recordName, std::ofstream &stream) const431 void 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 
ProcessToJson(uint32_t threshold, ProfileType::jModuleType &jModule) const469 void 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 
ParseFromBinary(PGOContext &context, void **buffer)490 bool 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 
GetMismatchResult(const CString &recordName, uint32_t &totalMethodCount, uint32_t &mismatchMethodCount, std::set<std::pair<std::string, CString>> &mismatchMethodSet) const524 void 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 
Merge(const PGOMethodIdSet &from)539 void 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 
Merge(const PGODecodeMethodInfo &from)551 void 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 
PGORecordDetailInfos(uint32_t hotnessThreshold)561 PGORecordDetailInfos::PGORecordDetailInfos(uint32_t hotnessThreshold) : hotnessThreshold_(hotnessThreshold)
562 {
563     chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_);
564     InitSections();
565 };
566 
~PGORecordDetailInfos()567 PGORecordDetailInfos::~PGORecordDetailInfos()
568 {
569     Clear();
570 }
571 
GetMethodInfoMap(ProfileType recordProfileType)572 PGOMethodInfoMap *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 
AddMethod(ProfileType recordProfileType, Method *jsMethod, SampleMode mode)584 bool 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 
AddType(ProfileType recordProfileType, PGOMethodId methodId, int32_t offset, PGOSampleType type)592 bool 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 
AddCallTargetType(ProfileType recordProfileType, PGOMethodId methodId, int32_t offset, PGOSampleType type)600 bool 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 
AddObjectInfo( ProfileType recordProfileType, EntityId methodId, int32_t offset, const PGOObjectInfo &info)608 bool 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 
AddDefine( ProfileType recordProfileType, PGOMethodId methodId, int32_t offset, PGODefineOpType type)616 bool 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 
AddRootLayout(JSTaggedType hclass, ProfileType rootType)634 bool 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 
UpdateLayout(ProfileType rootType, JSTaggedType hclass, ProfileType curType)649 bool 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 
UpdateTransitionLayout( ProfileType rootType, JSTaggedType parent, ProfileType parentType, JSTaggedType child, ProfileType childType)665 bool 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 
AddRootPtType(ProfileType rootType, ProfileType ptType)681 void 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 
IsDumped(ProfileType rootType, ProfileType curType) const693 bool 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 
Merge(const PGORecordDetailInfos &recordInfos)703 void 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 
ParseFromBinary(void *buffer, PGOProfilerHeader *const header)742 bool 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 
ParseFromBinaryForLayout(void **buffer)793 bool 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 
ProcessToBinary( const SaveTask *task, std::fstream &fileStream, PGOProfilerHeader *const header)807 void 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 
ProcessToBinaryForLayout( NativeAreaAllocator *allocator, const SaveTask *task, std::fstream &stream)847 bool 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 
ParseFromText(std::ifstream &stream)884 bool 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 
ProcessToText(std::ofstream &stream) const927 void 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 
InitSections()960 void PGORecordDetailInfos::InitSections()
961 {
962     recordPool_ = std::make_unique<PGORecordPool>();
963     protoTransitionPool_ = std::make_unique<PGOProtoTransitionPool>();
964     profileTypePool_ = std::make_unique<PGOProfileTypePool>();
965 }
966 
Clear()967 void 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 
Match(const CString &abcNormalizedDesc, const CString &recordName, EntityId methodId)987 bool 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 
ParseFromBinary(void *buffer, PGOProfilerHeader *const header, std::shared_ptr<PGOAbcFilePool> &abcFilePool)1003 void 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 
Merge(const PGORecordSimpleInfos &simpleInfos)1064 void 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 
ParseFromBinaryForLayout(void **buffer)1095 bool 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 
InitSections()1109 void PGORecordSimpleInfos::InitSections()
1110 {
1111     recordPool_ = std::make_unique<PGORecordPool>();
1112     protoTransitionPool_ = std::make_unique<PGOProtoTransitionPool>();
1113     profileTypePool_ = std::make_unique<PGOProfileTypePool>();
1114 }
1115 
Clear()1116 void 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 
PGORecordSimpleInfos(uint32_t threshold)1137 PGORecordSimpleInfos::PGORecordSimpleInfos(uint32_t threshold) : hotnessThreshold_(threshold)
1138 {
1139     chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_);
1140     InitSections();
1141 }
1142 
~PGORecordSimpleInfos()1143 PGORecordSimpleInfos::~PGORecordSimpleInfos()
1144 {
1145     Clear();
1146 }
1147 } // namespace panda::ecmascript::pgo
1148