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