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