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