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