1 /*
2 * Copyright (c) 2023 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
21 #include "ecmascript/base/bit_helper.h"
22 #include "ecmascript/base/file_header.h"
23 #include "ecmascript/js_function.h"
24 #include "ecmascript/jspandafile/method_literal.h"
25 #include "ecmascript/log_wrapper.h"
26 #include "ecmascript/mem/c_string.h"
27 #include "ecmascript/pgo_profiler/pgo_profiler_encoder.h"
28 #include "macros.h"
29 #include "securec.h"
30 #include "zlib.h"
31
32 namespace panda::ecmascript {
33 static const std::string ELEMENT_SEPARATOR = "/";
34 static const std::string BLOCK_SEPARATOR = ",";
35 static const std::string TYPE_SEPARATOR = "|";
36 static const std::string BLOCK_START = ":";
37 static const std::string ARRAY_START = "[";
38 static const std::string ARRAY_END = "]";
39 static const std::string NEW_LINE = "\n";
40 static const std::string SPACE = " ";
41 static const std::string BLOCK_AND_ARRAY_START = BLOCK_START + SPACE + ARRAY_START + SPACE;
42 static const std::string VERSION_HEADER = "Profiler Version" + BLOCK_START + SPACE;
43 static const std::string PANDA_FILE_INFO_HEADER = "Panda file sumcheck list" + BLOCK_AND_ARRAY_START;
44 static const uint32_t HEX_FORMAT_WIDTH_FOR_32BITS = 10; // for example, 0xffffffff is 10 characters
45
BuildFromLegacy(void * buffer,PGOProfilerHeader ** header)46 bool PGOProfilerHeader::BuildFromLegacy(void *buffer, PGOProfilerHeader **header)
47 {
48 auto *inHeader = reinterpret_cast<PGOProfilerHeaderLegacy *>(buffer);
49 size_t desSize = Size(inHeader->GetSectionNumber());
50 if (desSize > LastSize()) {
51 LOG_ECMA(ERROR) << "header size error, expected size is less than " << LastSize() << ", but got " << desSize;
52 return false;
53 }
54 Build(header, desSize);
55 // copy header base.
56 if (memcpy_s(*header, sizeof(FileHeaderBase), inHeader, sizeof(FileHeaderBase)) != EOK) {
57 UNREACHABLE();
58 }
59 // skip elastic header field, and copy section info from incoming buffer.
60 auto sectionSize = desSize - sizeof(FileHeaderElastic);
61 if (memcpy_s(&((*header)->sectionNumber_), sectionSize, &(inHeader->GetSectionNumber()), sectionSize) != EOK) {
62 UNREACHABLE();
63 }
64 return true;
65 }
66
BuildFromElastic(void * buffer,size_t bufferSize,PGOProfilerHeader ** header)67 bool PGOProfilerHeader::BuildFromElastic(void *buffer, size_t bufferSize, PGOProfilerHeader **header)
68 {
69 auto *inHeader = reinterpret_cast<PGOProfilerHeader *>(buffer);
70 if (!inHeader->Verify(buffer, bufferSize)) {
71 return false;
72 }
73 size_t desSize = inHeader->Size();
74 if (desSize > LastSize()) {
75 LOG_ECMA(ERROR) << "header size error, expected size is less than " << LastSize() << ", but got " << desSize;
76 return false;
77 }
78 Build(header, desSize);
79 if (memcpy_s(*header, desSize, inHeader, desSize) != EOK) {
80 UNREACHABLE();
81 }
82 return true;
83 }
84
ParseFromBinary(void * buffer,size_t bufferSize,PGOProfilerHeader ** header)85 bool PGOProfilerHeader::ParseFromBinary(void *buffer, size_t bufferSize, PGOProfilerHeader **header)
86 {
87 auto *inHeaderBase = reinterpret_cast<FileHeaderBase *>(buffer);
88 if (inHeaderBase->VerifyVersion("apPath file", LAST_VERSION, false)) {
89 if (!inHeaderBase->CompatibleVerify(ELASTIC_HEADER_MINI_VERSION)) {
90 return BuildFromLegacy(buffer, header);
91 }
92 return BuildFromElastic(buffer, bufferSize, header);
93 }
94 return false;
95 }
96
VerifyFileSize(size_t bufferSize) const97 bool PGOProfilerHeader::VerifyFileSize(size_t bufferSize) const
98 {
99 if (!SupportFileSize()) {
100 return true;
101 }
102 if (GetFileSize() != bufferSize) {
103 LOG_ECMA(ERROR) << "Verify ap file's file size failed. size: " << std::hex << bufferSize << " vs "
104 << GetFileSize();
105 return false;
106 }
107 return true;
108 }
109
VerifyConsistency(void * buffer,size_t bufferSize) const110 bool PGOProfilerHeader::VerifyConsistency(void *buffer, size_t bufferSize) const
111 {
112 if (!SupportFileConsistency()) {
113 return true;
114 }
115 uint32_t checksum = adler32(0, reinterpret_cast<const Bytef *>(buffer) + MAGIC_SIZE, VERSION_SIZE);
116 checksum = adler32(checksum, reinterpret_cast<const Bytef *>(buffer) + CHECKSUM_END_OFFSET,
117 bufferSize - CHECKSUM_END_OFFSET);
118 if (checksum != GetChecksum()) {
119 LOG_ECMA(ERROR) << "Verify ap file's consistency failed. checksum: " << std::hex << checksum << " vs "
120 << std::hex << GetChecksum();
121 return false;
122 }
123 return true;
124 }
125
ProcessToBinary(std::fstream & fileStream) const126 void PGOProfilerHeader::ProcessToBinary(std::fstream &fileStream) const
127 {
128 fileStream.seekp(0);
129 if (base::FileHeaderBase::CompatibleVerify(ELASTIC_HEADER_MINI_VERSION)) {
130 fileStream.write(reinterpret_cast<const char *>(this), Size());
131 } else {
132 // copy header base.
133 fileStream.write(reinterpret_cast<const char *>(this), sizeof(FileHeaderBase));
134 // skip elastic header field, and copy section info from incoming buffer.
135 auto sectionSize = Size() - sizeof(FileHeaderElastic);
136 fileStream.write(reinterpret_cast<const char *>(§ionNumber_), sectionSize);
137 }
138 }
139
ParseFromText(std::ifstream & stream)140 bool PGOProfilerHeader::ParseFromText(std::ifstream &stream)
141 {
142 std::string header;
143 if (std::getline(stream, header)) {
144 if (header.empty()) {
145 return false;
146 }
147 auto index = header.find(BLOCK_START);
148 if (index == std::string::npos) {
149 return false;
150 }
151 auto version = header.substr(index + 1);
152 if (!InternalSetVersion(version)) {
153 return false;
154 }
155 if (!Verify()) {
156 return false;
157 }
158 if (!base::FileHeaderBase::CompatibleVerify(ELASTIC_HEADER_MINI_VERSION)) {
159 auto *pandaInfoSection = GetPandaInfoSection();
160 pandaInfoSection->offset_ -= sizeof(PGOProfilerHeader) - sizeof(PGOProfilerHeaderLegacy);
161 }
162 return true;
163 }
164 return false;
165 }
166
ProcessToText(std::ofstream & stream) const167 bool PGOProfilerHeader::ProcessToText(std::ofstream &stream) const
168 {
169 if (!Verify()) {
170 return false;
171 }
172 stream << VERSION_HEADER << InternalGetVersion() << NEW_LINE;
173 if (SupportFileConsistency()) {
174 stream << "FileSize: " << GetFileSize() << " ,HeaderSize: " << GetHeaderSize() << " ,Checksum: " << std::hex
175 << GetChecksum() << NEW_LINE;
176 }
177 return true;
178 }
179
ParseFromBinary(void * buffer,SectionInfo * const info)180 void PGOPandaFileInfos::ParseFromBinary(void *buffer, SectionInfo *const info)
181 {
182 void *addr = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + info->offset_);
183 for (uint32_t i = 0; i < info->number_; i++) {
184 fileInfos_.emplace(*base::ReadBufferInSize<FileInfo>(&addr));
185 }
186 LOG_ECMA(DEBUG) << "Profiler panda file count:" << info->number_;
187 }
188
ProcessToBinary(std::fstream & fileStream,SectionInfo * info) const189 void PGOPandaFileInfos::ProcessToBinary(std::fstream &fileStream, SectionInfo *info) const
190 {
191 fileStream.seekp(info->offset_);
192 info->number_ = fileInfos_.size();
193 for (auto localInfo : fileInfos_) {
194 fileStream.write(reinterpret_cast<char *>(&localInfo), localInfo.Size());
195 }
196 info->size_ = static_cast<uint32_t>(fileStream.tellp()) - info->offset_;
197 }
198
Merge(const PGOPandaFileInfos & pandaFileInfos)199 void PGOPandaFileInfos::Merge(const PGOPandaFileInfos &pandaFileInfos)
200 {
201 for (const auto &info : pandaFileInfos.fileInfos_) {
202 fileInfos_.emplace(info.GetChecksum());
203 }
204 }
205
VerifyChecksum(const PGOPandaFileInfos & pandaFileInfos,const std::string & base,const std::string & incoming) const206 bool PGOPandaFileInfos::VerifyChecksum(const PGOPandaFileInfos &pandaFileInfos, const std::string &base,
207 const std::string &incoming) const
208 {
209 std::set<FileInfo> unionChecksum;
210 set_union(fileInfos_.begin(), fileInfos_.end(), pandaFileInfos.fileInfos_.begin(), pandaFileInfos.fileInfos_.end(),
211 inserter(unionChecksum, unionChecksum.begin()));
212 if (!fileInfos_.empty() && unionChecksum.empty()) {
213 LOG_ECMA(ERROR) << "First AP file(" << base << ") and the incoming file(" << incoming
214 << ") do not come from the same abc file, skip merge the incoming file.";
215 return false;
216 }
217 return true;
218 }
219
ParseFromText(std::ifstream & stream)220 bool PGOPandaFileInfos::ParseFromText(std::ifstream &stream)
221 {
222 std::string pandaFileInfo;
223 while (std::getline(stream, pandaFileInfo)) {
224 if (pandaFileInfo.empty()) {
225 continue;
226 }
227
228 size_t start = pandaFileInfo.find_first_of(ARRAY_START);
229 size_t end = pandaFileInfo.find_last_of(ARRAY_END);
230 if (start == std::string::npos || end == std::string::npos || start > end) {
231 return false;
232 }
233 auto content = pandaFileInfo.substr(start + 1, end - (start + 1) - 1);
234 std::vector<std::string> infos = base::StringHelper::SplitString(content, BLOCK_SEPARATOR);
235 for (auto checksum : infos) {
236 uint32_t result;
237 if (!base::StringHelper::StrToUInt32(checksum.c_str(), &result)) {
238 LOG_ECMA(ERROR) << "checksum: " << checksum << " parse failed";
239 return false;
240 }
241 Sample(result);
242 }
243 return true;
244 }
245 return true;
246 }
247
ProcessToText(std::ofstream & stream) const248 void PGOPandaFileInfos::ProcessToText(std::ofstream &stream) const
249 {
250 std::string pandaFileInfo = NEW_LINE + PANDA_FILE_INFO_HEADER;
251 bool isFirst = true;
252 for (auto &info : fileInfos_) {
253 if (!isFirst) {
254 pandaFileInfo += BLOCK_SEPARATOR + SPACE;
255 } else {
256 isFirst = false;
257 }
258 pandaFileInfo += std::to_string(info.GetChecksum());
259 }
260
261 pandaFileInfo += (SPACE + ARRAY_END + NEW_LINE);
262 stream << pandaFileInfo;
263 }
264
Checksum(uint32_t checksum) const265 bool PGOPandaFileInfos::Checksum(uint32_t checksum) const
266 {
267 if (fileInfos_.find(checksum) == fileInfos_.end()) {
268 LOG_ECMA(ERROR) << "Checksum verification failed. Please ensure that the .abc and .ap match.";
269 return false;
270 }
271 return true;
272 }
273
ProcessToText(std::string & text) const274 void PGOMethodInfo::ProcessToText(std::string &text) const
275 {
276 text += std::to_string(GetMethodId().GetOffset());
277 text += ELEMENT_SEPARATOR;
278 text += std::to_string(GetCount());
279 text += ELEMENT_SEPARATOR;
280 text += GetSampleModeToString();
281 text += ELEMENT_SEPARATOR;
282 text += GetMethodName();
283 }
284
ParseFromText(const std::string & infoString)285 std::vector<std::string> PGOMethodInfo::ParseFromText(const std::string &infoString)
286 {
287 std::vector<std::string> infoStrings = base::StringHelper::SplitString(infoString, ELEMENT_SEPARATOR);
288 return infoStrings;
289 }
290
CalcChecksum(const char * name,const uint8_t * byteCodeArray,uint32_t byteCodeLength)291 uint32_t PGOMethodInfo::CalcChecksum(const char *name, const uint8_t *byteCodeArray, uint32_t byteCodeLength)
292 {
293 uint32_t checksum = 0;
294 if (byteCodeArray != nullptr) {
295 checksum = CalcOpCodeChecksum(byteCodeArray, byteCodeLength);
296 }
297
298 if (name != nullptr) {
299 checksum = adler32(checksum, reinterpret_cast<const Bytef *>(name), strlen(name));
300 }
301 return checksum;
302 }
303
CalcOpCodeChecksum(const uint8_t * byteCodeArray,uint32_t byteCodeLength)304 uint32_t PGOMethodInfo::CalcOpCodeChecksum(const uint8_t *byteCodeArray, uint32_t byteCodeLength)
305 {
306 uint32_t checksum = 0;
307 BytecodeInstruction bcIns(byteCodeArray);
308 auto bcInsLast = bcIns.JumpTo(byteCodeLength);
309 while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
310 auto opCode = bcIns.GetOpcode();
311 checksum = adler32(checksum, reinterpret_cast<const Bytef *>(&opCode), sizeof(decltype(opCode)));
312 bcIns = bcIns.GetNext();
313 }
314 return checksum;
315 }
316
Merge(const PGOMethodTypeSet * info)317 void PGOMethodTypeSet::Merge(const PGOMethodTypeSet *info)
318 {
319 for (const auto &fromType : info->scalarOpTypeInfos_) {
320 auto iter = scalarOpTypeInfos_.find(fromType);
321 if (iter != scalarOpTypeInfos_.end()) {
322 const_cast<ScalarOpTypeInfo &>(*iter).Merge(fromType);
323 } else {
324 scalarOpTypeInfos_.emplace(fromType);
325 }
326 }
327 for (const auto &fromType : info->rwScalarOpTypeInfos_) {
328 auto iter = rwScalarOpTypeInfos_.find(fromType);
329 if (iter != rwScalarOpTypeInfos_.end()) {
330 const_cast<RWScalarOpTypeInfo &>(*iter).Merge(fromType);
331 } else {
332 rwScalarOpTypeInfos_.emplace(fromType);
333 }
334 }
335 for (const auto &fromType : info->objDefOpTypeInfos_) {
336 AddDefine(fromType.GetOffset(), fromType.GetType(), fromType.GetSuperType());
337 }
338 }
339
SkipFromBinary(void ** buffer)340 void PGOMethodTypeSet::SkipFromBinary(void **buffer)
341 {
342 uint32_t size = base::ReadBuffer<uint32_t>(buffer, sizeof(uint32_t));
343 for (uint32_t i = 0; i < size; i++) {
344 base::ReadBufferInSize<ScalarOpTypeInfo>(buffer);
345 }
346 }
347
ParseFromBinary(void ** buffer,PGOProfilerHeader * const header)348 bool PGOMethodTypeSet::ParseFromBinary(void **buffer, PGOProfilerHeader *const header)
349 {
350 uint32_t size = base::ReadBuffer<uint32_t>(buffer, sizeof(uint32_t));
351 for (uint32_t i = 0; i < size; i++) {
352 auto typeInfo = base::ReadBufferInSize<TypeInfoHeader>(buffer);
353 if (typeInfo->GetInfoType() == InfoType::OP_TYPE) {
354 scalarOpTypeInfos_.emplace(*reinterpret_cast<ScalarOpTypeInfo *>(typeInfo));
355 } else if (typeInfo->GetInfoType() == InfoType::DEFINE_CLASS_TYPE) {
356 objDefOpTypeInfos_.emplace(*reinterpret_cast<ObjDefOpTypeInfo *>(typeInfo));
357 } else if (header->SupportUseHClassType() && typeInfo->GetInfoType() == InfoType::USE_HCLASS_TYPE) {
358 rwScalarOpTypeInfos_.emplace(*reinterpret_cast<RWScalarOpTypeInfo *>(typeInfo));
359 }
360 }
361 return true;
362 }
363
ProcessToBinary(std::stringstream & stream) const364 bool PGOMethodTypeSet::ProcessToBinary(std::stringstream &stream) const
365 {
366 uint32_t number = 0;
367 std::stringstream methodStream;
368 for (auto typeInfo : scalarOpTypeInfos_) {
369 if (!typeInfo.GetType().IsNone()) {
370 methodStream.write(reinterpret_cast<char *>(&typeInfo), typeInfo.Size());
371 number++;
372 }
373 }
374 for (auto typeInfo : rwScalarOpTypeInfos_) {
375 if (typeInfo.GetCount() != 0) {
376 methodStream.write(reinterpret_cast<char *>(&typeInfo), typeInfo.Size());
377 number++;
378 }
379 }
380
381 for (auto typeInfo : objDefOpTypeInfos_) {
382 methodStream.write(reinterpret_cast<char *>(&typeInfo), typeInfo.Size());
383 number++;
384 }
385
386 stream.write(reinterpret_cast<char *>(&number), sizeof(uint32_t));
387 if (number > 0) {
388 stream << methodStream.rdbuf();
389 return true;
390 }
391 return false;
392 }
393
ParseFromText(const std::string & typeString)394 bool PGOMethodTypeSet::ParseFromText(const std::string &typeString)
395 {
396 std::vector<std::string> typeInfoVector = base::StringHelper::SplitString(typeString, TYPE_SEPARATOR);
397 if (typeInfoVector.size() > 0) {
398 for (const auto &iter : typeInfoVector) {
399 std::vector<std::string> typeStrings = base::StringHelper::SplitString(iter, BLOCK_START);
400 if (typeStrings.size() < METHOD_TYPE_INFO_COUNT) {
401 return false;
402 }
403
404 uint32_t offset;
405 if (!base::StringHelper::StrToUInt32(typeStrings[METHOD_OFFSET_INDEX].c_str(), &offset)) {
406 return false;
407 }
408 uint32_t type;
409 if (!base::StringHelper::StrToUInt32(typeStrings[METHOD_TYPE_INDEX].c_str(), &type)) {
410 return false;
411 }
412 scalarOpTypeInfos_.emplace(offset, PGOSampleType(type));
413 }
414 }
415 return true;
416 }
417
ProcessToText(std::string & text) const418 void PGOMethodTypeSet::ProcessToText(std::string &text) const
419 {
420 bool isFirst = true;
421 for (auto typeInfoIter : scalarOpTypeInfos_) {
422 if (typeInfoIter.GetType().IsNone()) {
423 continue;
424 }
425 if (isFirst) {
426 text += ARRAY_START + SPACE;
427 isFirst = false;
428 } else {
429 text += TYPE_SEPARATOR + SPACE;
430 }
431 text += std::to_string(typeInfoIter.GetOffset());
432 text += BLOCK_START;
433 text += typeInfoIter.GetType().GetTypeString();
434 }
435 for (auto rwScalarOpTypeInfoIter : rwScalarOpTypeInfos_) {
436 if (rwScalarOpTypeInfoIter.GetCount() == 0) {
437 continue;
438 }
439 if (isFirst) {
440 text += ARRAY_START + SPACE;
441 isFirst = false;
442 } else {
443 text += TYPE_SEPARATOR + SPACE;
444 }
445 rwScalarOpTypeInfoIter.ProcessToText(text);
446 }
447 for (const auto &defTypeInfoIter : objDefOpTypeInfos_) {
448 if (isFirst) {
449 text += ARRAY_START + SPACE;
450 isFirst = false;
451 } else {
452 text += TYPE_SEPARATOR + SPACE;
453 }
454 defTypeInfoIter.ProcessToText(text);
455 }
456 if (!isFirst) {
457 text += (SPACE + ARRAY_END);
458 }
459 }
460
CaculateSize(const PGOHClassLayoutDesc & desc)461 size_t PGOHClassLayoutDescInner::CaculateSize(const PGOHClassLayoutDesc &desc)
462 {
463 if (desc.GetLayoutDesc().empty() && desc.GetPtLayoutDesc().empty() && desc.GetCtorLayoutDesc().empty()) {
464 return sizeof(PGOHClassLayoutDescInner);
465 }
466 size_t size = sizeof(PGOHClassLayoutDescInner) - sizeof(PGOLayoutDescInfo);
467 for (const auto &iter : desc.GetLayoutDesc()) {
468 auto key = iter.first;
469 if (key.size() > 0) {
470 size += static_cast<size_t>(PGOLayoutDescInfo::Size(key.size()));
471 }
472 }
473 for (const auto &iter : desc.GetPtLayoutDesc()) {
474 auto key = iter.first;
475 if (key.size() > 0) {
476 size += static_cast<size_t>(PGOLayoutDescInfo::Size(key.size()));
477 }
478 }
479 for (const auto &iter : desc.GetCtorLayoutDesc()) {
480 auto key = iter.first;
481 if (key.size() > 0) {
482 size += static_cast<size_t>(PGOLayoutDescInfo::Size(key.size()));
483 }
484 }
485 size += sizeof(ElementsKind);
486 return size;
487 }
488
GetTypeString(const PGOHClassLayoutDesc & desc)489 std::string PGOHClassLayoutDescInner::GetTypeString(const PGOHClassLayoutDesc &desc)
490 {
491 std::string text;
492 text += desc.GetClassType().GetTypeString();
493 if (!desc.GetSuperClassType().IsNone()) {
494 text += TYPE_SEPARATOR + SPACE;
495 text += desc.GetSuperClassType().GetTypeString();
496 }
497 if (!Elements::IsNone(desc.GetElementsKind())) {
498 text += TYPE_SEPARATOR + SPACE;
499 text += Elements::GetString(desc.GetElementsKind());
500 }
501 text += BLOCK_AND_ARRAY_START;
502 bool isLayoutFirst = true;
503 for (const auto &layoutDesc : desc.GetLayoutDesc()) {
504 if (!isLayoutFirst) {
505 text += TYPE_SEPARATOR + SPACE;
506 } else {
507 text += ARRAY_START;
508 }
509 isLayoutFirst = false;
510 text += layoutDesc.first;
511 text += BLOCK_START;
512 text += std::to_string(layoutDesc.second.GetValue());
513 }
514 if (!isLayoutFirst) {
515 text += ARRAY_END;
516 }
517 bool isPtLayoutFirst = true;
518 for (const auto &layoutDesc : desc.GetPtLayoutDesc()) {
519 if (!isPtLayoutFirst) {
520 text += TYPE_SEPARATOR + SPACE;
521 } else {
522 if (!isLayoutFirst) {
523 text += TYPE_SEPARATOR + SPACE;
524 }
525 text += ARRAY_START;
526 }
527 isPtLayoutFirst = false;
528 text += layoutDesc.first;
529 text += BLOCK_START;
530 text += std::to_string(layoutDesc.second.GetValue());
531 }
532 if (!isPtLayoutFirst) {
533 text += ARRAY_END;
534 }
535 bool isCtorLayoutFirst = true;
536 for (const auto &layoutDesc : desc.GetCtorLayoutDesc()) {
537 if (!isCtorLayoutFirst) {
538 text += TYPE_SEPARATOR + SPACE;
539 } else {
540 if (!isLayoutFirst || !isPtLayoutFirst) {
541 text += TYPE_SEPARATOR + SPACE;
542 }
543 text += ARRAY_START;
544 }
545 isCtorLayoutFirst = false;
546 text += layoutDesc.first;
547 text += BLOCK_START;
548 text += std::to_string(layoutDesc.second.GetValue());
549 }
550 if (!isCtorLayoutFirst) {
551 text += ARRAY_END;
552 }
553 text += (SPACE + ARRAY_END);
554 return text;
555 }
556
Merge(const PGOHClassLayoutDesc & desc)557 void PGOHClassLayoutDescInner::Merge(const PGOHClassLayoutDesc &desc)
558 {
559 auto current = const_cast<PGOLayoutDescInfo *>(GetFirst());
560 for (const auto &iter : desc.GetLayoutDesc()) {
561 auto key = iter.first;
562 auto type = iter.second;
563 if (key.size() > 0) {
564 new (current) PGOLayoutDescInfo(key, type);
565 current = const_cast<PGOLayoutDescInfo *>(GetNext(current));
566 count_++;
567 }
568 }
569 for (const auto &iter : desc.GetPtLayoutDesc()) {
570 auto key = iter.first;
571 auto type = iter.second;
572 if (key.size() > 0) {
573 new (current) PGOLayoutDescInfo(key, type);
574 current = const_cast<PGOLayoutDescInfo *>(GetNext(current));
575 ptCount_++;
576 }
577 }
578 for (const auto &iter : desc.GetCtorLayoutDesc()) {
579 auto key = iter.first;
580 auto type = iter.second;
581 if (key.size() > 0) {
582 new (current) PGOLayoutDescInfo(key, type);
583 current = const_cast<PGOLayoutDescInfo *>(GetNext(current));
584 ctorCount_++;
585 }
586 }
587 }
588
ProcessToText(std::string & text) const589 void PGOMethodTypeSet::RWScalarOpTypeInfo::ProcessToText(std::string &text) const
590 {
591 text += std::to_string(GetOffset());
592 text += BLOCK_START;
593 text += ARRAY_START + SPACE;
594 bool isFirst = true;
595 for (uint32_t i = 0; i < type_.GetCount(); i++) {
596 if (!isFirst) {
597 text += TYPE_SEPARATOR + SPACE;
598 }
599 isFirst = false;
600 text += type_.GetObjectInfo(i).GetInfoString();
601 }
602 text += (SPACE + ARRAY_END);
603 }
604
ProcessToText(std::string & text) const605 void PGOMethodTypeSet::ObjDefOpTypeInfo::ProcessToText(std::string &text) const
606 {
607 text += std::to_string(GetOffset());
608 text += BLOCK_START;
609 text += ARRAY_START + SPACE;
610 text += GetType().GetTypeString();
611 text += TYPE_SEPARATOR;
612 text += GetSuperType().GetTypeString();
613 text += (SPACE + ARRAY_END);
614 }
615
AddMethod(Chunk * chunk,Method * jsMethod,SampleMode mode,int32_t incCount)616 bool PGOMethodInfoMap::AddMethod(Chunk *chunk, Method *jsMethod, SampleMode mode, int32_t incCount)
617 {
618 PGOMethodId methodId(jsMethod->GetMethodId());
619 auto result = methodInfos_.find(methodId);
620 if (result != methodInfos_.end()) {
621 auto info = result->second;
622 info->IncreaseCount(incCount);
623 info->SetSampleMode(mode);
624 return false;
625 } else {
626 CString methodName = jsMethod->GetMethodName();
627 size_t strlen = methodName.size();
628 size_t size = static_cast<size_t>(PGOMethodInfo::Size(strlen));
629 void *infoAddr = chunk->Allocate(size);
630 auto info = new (infoAddr) PGOMethodInfo(methodId, incCount, mode, methodName.c_str());
631 methodInfos_.emplace(methodId, info);
632 auto checksum = PGOMethodInfo::CalcChecksum(jsMethod->GetMethodName(), jsMethod->GetBytecodeArray(),
633 jsMethod->GetCodeSize());
634 methodsChecksum_.emplace(methodId, checksum);
635 return true;
636 }
637 }
638
GetOrInsertMethodTypeSet(Chunk * chunk,PGOMethodId methodId)639 PGOMethodTypeSet *PGOMethodInfoMap::GetOrInsertMethodTypeSet(Chunk *chunk, PGOMethodId methodId)
640 {
641 auto typeInfoSetIter = methodTypeInfos_.find(methodId);
642 if (typeInfoSetIter != methodTypeInfos_.end()) {
643 return typeInfoSetIter->second;
644 } else {
645 auto typeInfoSet = chunk->New<PGOMethodTypeSet>();
646 methodTypeInfos_.emplace(methodId, typeInfoSet);
647 return typeInfoSet;
648 }
649 }
650
AddType(Chunk * chunk,PGOMethodId methodId,int32_t offset,PGOSampleType type)651 bool PGOMethodInfoMap::AddType(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type)
652 {
653 auto typeInfoSet = GetOrInsertMethodTypeSet(chunk, methodId);
654 ASSERT(typeInfoSet != nullptr);
655 typeInfoSet->AddType(offset, type);
656 return true;
657 }
658
AddCallTargetType(Chunk * chunk,PGOMethodId methodId,int32_t offset,PGOSampleType type)659 bool PGOMethodInfoMap::AddCallTargetType(Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type)
660 {
661 auto typeInfoSet = GetOrInsertMethodTypeSet(chunk, methodId);
662 ASSERT(typeInfoSet != nullptr);
663 typeInfoSet->AddCallTargetType(offset, type);
664 return true;
665 }
666
AddObjectInfo(Chunk * chunk,PGOMethodId methodId,int32_t offset,const PGOObjectInfo & info)667 bool PGOMethodInfoMap::AddObjectInfo(Chunk *chunk, PGOMethodId methodId, int32_t offset, const PGOObjectInfo &info)
668 {
669 auto typeInfoSet = GetOrInsertMethodTypeSet(chunk, methodId);
670 ASSERT(typeInfoSet != nullptr);
671 typeInfoSet->AddObjectInfo(offset, info);
672 return true;
673 }
674
AddDefine(Chunk * chunk,PGOMethodId methodId,int32_t offset,PGOSampleType type,PGOSampleType superType)675 bool PGOMethodInfoMap::AddDefine(
676 Chunk *chunk, PGOMethodId methodId, int32_t offset, PGOSampleType type, PGOSampleType superType)
677 {
678 auto typeInfoSet = GetOrInsertMethodTypeSet(chunk, methodId);
679 ASSERT(typeInfoSet != nullptr);
680 typeInfoSet->AddDefine(offset, type, superType);
681 return true;
682 }
683
Merge(Chunk * chunk,PGOMethodInfoMap * methodInfos)684 void PGOMethodInfoMap::Merge(Chunk *chunk, PGOMethodInfoMap *methodInfos)
685 {
686 for (auto iter = methodInfos->methodInfos_.begin(); iter != methodInfos->methodInfos_.end(); iter++) {
687 auto methodId = iter->first;
688 auto fromMethodInfo = iter->second;
689
690 auto result = methodInfos_.find(methodId);
691 if (result != methodInfos_.end()) {
692 auto toMethodInfo = result->second;
693 toMethodInfo->Merge(fromMethodInfo);
694 } else {
695 size_t len = strlen(fromMethodInfo->GetMethodName());
696 size_t size = static_cast<size_t>(PGOMethodInfo::Size(len));
697 void *infoAddr = chunk->Allocate(size);
698 auto newMethodInfo = new (infoAddr) PGOMethodInfo(
699 methodId, fromMethodInfo->GetCount(), fromMethodInfo->GetSampleMode(), fromMethodInfo->GetMethodName());
700 methodInfos_.emplace(methodId, newMethodInfo);
701 }
702 fromMethodInfo->ClearCount();
703 }
704
705 for (auto iter = methodInfos->methodTypeInfos_.begin(); iter != methodInfos->methodTypeInfos_.end(); iter++) {
706 auto methodId = iter->first;
707 auto fromTypeInfo = iter->second;
708
709 auto result = methodTypeInfos_.find(methodId);
710 if (result != methodTypeInfos_.end()) {
711 auto toTypeInfo = result->second;
712 toTypeInfo->Merge(fromTypeInfo);
713 } else {
714 auto typeInfoSet = chunk->New<PGOMethodTypeSet>();
715 typeInfoSet->Merge(fromTypeInfo);
716 methodTypeInfos_.emplace(methodId, typeInfoSet);
717 }
718 }
719
720 for (auto iter = methodInfos->methodsChecksum_.begin(); iter != methodInfos->methodsChecksum_.end(); iter++) {
721 auto methodId = iter->first;
722 auto result = methodsChecksum_.find(methodId);
723 if (result == methodsChecksum_.end()) {
724 methodsChecksum_.emplace(methodId, iter->second);
725 }
726 }
727 }
728
ParseFromBinary(Chunk * chunk,uint32_t threshold,void ** buffer,PGOProfilerHeader * const header)729 bool PGOMethodInfoMap::ParseFromBinary(Chunk *chunk, uint32_t threshold, void **buffer, PGOProfilerHeader *const header)
730 {
731 SectionInfo secInfo = base::ReadBuffer<SectionInfo>(buffer);
732 for (uint32_t j = 0; j < secInfo.number_; j++) {
733 PGOMethodInfo *info = base::ReadBufferInSize<PGOMethodInfo>(buffer);
734 if (info->IsFilter(threshold)) {
735 if (header->SupportMethodChecksum()) {
736 base::ReadBuffer<uint32_t>(buffer, sizeof(uint32_t));
737 }
738 if (header->SupportType()) {
739 PGOMethodTypeSet::SkipFromBinary(buffer);
740 }
741 continue;
742 }
743 methodInfos_.emplace(info->GetMethodId(), info);
744 LOG_ECMA(DEBUG) << "Method:" << info->GetMethodId() << ELEMENT_SEPARATOR << info->GetCount()
745 << ELEMENT_SEPARATOR << std::to_string(static_cast<int>(info->GetSampleMode()))
746 << ELEMENT_SEPARATOR << info->GetMethodName();
747 if (header->SupportMethodChecksum()) {
748 auto checksum = base::ReadBuffer<uint32_t>(buffer, sizeof(uint32_t));
749 methodsChecksum_.emplace(info->GetMethodId(), checksum);
750 }
751 if (header->SupportType()) {
752 auto typeInfoSet = chunk->New<PGOMethodTypeSet>();
753 typeInfoSet->ParseFromBinary(buffer, header);
754 methodTypeInfos_.emplace(info->GetMethodId(), typeInfoSet);
755 }
756 }
757 return !methodInfos_.empty();
758 }
759
ProcessToBinary(uint32_t threshold,const CString & recordName,const SaveTask * task,std::fstream & stream,PGOProfilerHeader * const header) const760 bool PGOMethodInfoMap::ProcessToBinary(uint32_t threshold, const CString &recordName, const SaveTask *task,
761 std::fstream &stream, PGOProfilerHeader *const header) const
762 {
763 SectionInfo secInfo;
764 std::stringstream methodStream;
765 for (auto iter = methodInfos_.begin(); iter != methodInfos_.end(); iter++) {
766 LOG_ECMA(DEBUG) << "Method:" << iter->first << ELEMENT_SEPARATOR << iter->second->GetCount()
767 << ELEMENT_SEPARATOR << std::to_string(static_cast<int>(iter->second->GetSampleMode()))
768 << ELEMENT_SEPARATOR << iter->second->GetMethodName();
769 if (task && task->IsTerminate()) {
770 LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate";
771 return false;
772 }
773 auto curMethodInfo = iter->second;
774 if (curMethodInfo->IsFilter(threshold)) {
775 continue;
776 }
777 methodStream.write(reinterpret_cast<char *>(curMethodInfo), curMethodInfo->Size());
778 if (header->SupportMethodChecksum()) {
779 auto checksumIter = methodsChecksum_.find(curMethodInfo->GetMethodId());
780 uint32_t checksum = 0;
781 if (checksumIter != methodsChecksum_.end()) {
782 checksum = checksumIter->second;
783 }
784 methodStream.write(reinterpret_cast<char *>(&checksum), sizeof(uint32_t));
785 }
786 if (header->SupportType()) {
787 auto typeInfoIter = methodTypeInfos_.find(curMethodInfo->GetMethodId());
788 if (typeInfoIter != methodTypeInfos_.end()) {
789 typeInfoIter->second->ProcessToBinary(methodStream);
790 } else {
791 uint32_t number = 0;
792 methodStream.write(reinterpret_cast<char *>(&number), sizeof(uint32_t));
793 }
794 }
795 secInfo.number_++;
796 }
797 if (secInfo.number_ > 0) {
798 secInfo.offset_ = sizeof(SectionInfo);
799 secInfo.size_ = static_cast<uint32_t>(methodStream.tellg());
800 stream << recordName << '\0';
801 stream.write(reinterpret_cast<char *>(&secInfo), sizeof(SectionInfo));
802 stream << methodStream.rdbuf();
803 return true;
804 }
805 return false;
806 }
807
ParseFromText(Chunk * chunk,uint32_t threshold,const std::vector<std::string> & content)808 bool PGOMethodInfoMap::ParseFromText(Chunk *chunk, uint32_t threshold, const std::vector<std::string> &content)
809 {
810 for (auto infoString : content) {
811 std::vector<std::string> infoStrings = PGOMethodInfo::ParseFromText(infoString);
812 if (infoStrings.size() < PGOMethodInfo::METHOD_INFO_COUNT) {
813 LOG_ECMA(ERROR) << "method info:" << infoString << " format error";
814 return false;
815 }
816 uint32_t count;
817 if (!base::StringHelper::StrToUInt32(infoStrings[PGOMethodInfo::METHOD_COUNT_INDEX].c_str(), &count)) {
818 LOG_ECMA(ERROR) << "count: " << infoStrings[PGOMethodInfo::METHOD_COUNT_INDEX] << " parse failed";
819 return false;
820 }
821 SampleMode mode;
822 if (!PGOMethodInfo::GetSampleMode(infoStrings[PGOMethodInfo::METHOD_MODE_INDEX], mode)) {
823 LOG_ECMA(ERROR) << "mode: " << infoStrings[PGOMethodInfo::METHOD_MODE_INDEX] << " parse failed";
824 return false;
825 }
826 if (count < threshold && mode == SampleMode::CALL_MODE) {
827 return true;
828 }
829 uint32_t methodId;
830 if (!base::StringHelper::StrToUInt32(infoStrings[PGOMethodInfo::METHOD_ID_INDEX].c_str(), &methodId)) {
831 LOG_ECMA(ERROR) << "method id: " << infoStrings[PGOMethodInfo::METHOD_ID_INDEX] << " parse failed";
832 return false;
833 }
834 std::string methodName = infoStrings[PGOMethodInfo::METHOD_NAME_INDEX];
835
836 size_t len = methodName.size();
837 void *infoAddr = chunk->Allocate(PGOMethodInfo::Size(len));
838 auto info = new (infoAddr) PGOMethodInfo(PGOMethodId(methodId), count, mode, methodName.c_str());
839 methodInfos_.emplace(methodId, info);
840
841 // Parse Type Info
842 if (infoStrings.size() <= PGOMethodTypeSet::METHOD_TYPE_INFO_INDEX) {
843 continue;
844 }
845 std::string typeInfos = infoStrings[PGOMethodTypeSet::METHOD_TYPE_INFO_INDEX];
846 if (!typeInfos.empty()) {
847 size_t start = typeInfos.find_first_of(ARRAY_START);
848 size_t end = typeInfos.find_last_of(ARRAY_END);
849 if (start == std::string::npos || end == std::string::npos || start > end) {
850 LOG_ECMA(ERROR) << "Type info: " << typeInfos << " parse failed";
851 return false;
852 }
853 auto typeContent = typeInfos.substr(start + 1, end - (start + 1) - 1);
854 auto typeInfoSet = chunk->New<PGOMethodTypeSet>();
855 if (!typeInfoSet->ParseFromText(typeContent)) {
856 // delete by chunk
857 LOG_ECMA(ERROR) << "Type info: " << typeInfos << " parse failed";
858 return false;
859 }
860 methodTypeInfos_.emplace(info->GetMethodId(), typeInfoSet);
861 }
862 }
863
864 return true;
865 }
866
ProcessToText(uint32_t threshold,const CString & recordName,std::ofstream & stream) const867 void PGOMethodInfoMap::ProcessToText(uint32_t threshold, const CString &recordName, std::ofstream &stream) const
868 {
869 std::string profilerString;
870 bool isFirst = true;
871 for (auto methodInfoIter : methodInfos_) {
872 auto methodInfo = methodInfoIter.second;
873 if (methodInfo->IsFilter(threshold)) {
874 continue;
875 }
876 if (isFirst) {
877 profilerString += NEW_LINE;
878 profilerString += recordName;
879 profilerString += BLOCK_AND_ARRAY_START;
880 isFirst = false;
881 } else {
882 profilerString += BLOCK_SEPARATOR + SPACE;
883 }
884 methodInfo->ProcessToText(profilerString);
885 profilerString += ELEMENT_SEPARATOR;
886 auto checksumIter = methodsChecksum_.find(methodInfo->GetMethodId());
887 if (checksumIter != methodsChecksum_.end()) {
888 std::stringstream parseStream;
889 parseStream << std::internal << std::setfill('0') << std::showbase << std::setw(HEX_FORMAT_WIDTH_FOR_32BITS)
890 << std::hex << checksumIter->second << ELEMENT_SEPARATOR;
891 profilerString += parseStream.str();
892 }
893 auto iter = methodTypeInfos_.find(methodInfo->GetMethodId());
894 if (iter != methodTypeInfos_.end()) {
895 iter->second->ProcessToText(profilerString);
896 }
897 }
898 if (!isFirst) {
899 profilerString += (SPACE + ARRAY_END + NEW_LINE);
900 stream << profilerString;
901 }
902 }
903
ParseFromBinary(uint32_t threshold,void ** buffer,PGOProfilerHeader * const header)904 bool PGOMethodIdSet::ParseFromBinary(uint32_t threshold, void **buffer, PGOProfilerHeader *const header)
905 {
906 SectionInfo secInfo = base::ReadBuffer<SectionInfo>(buffer);
907 for (uint32_t j = 0; j < secInfo.number_; j++) {
908 PGOMethodInfo *info = base::ReadBufferInSize<PGOMethodInfo>(buffer);
909 if (info->IsFilter(threshold)) {
910 if (header->SupportMethodChecksum()) {
911 base::ReadBuffer<uint32_t>(buffer, sizeof(uint32_t));
912 }
913 if (header->SupportType()) {
914 PGOMethodTypeSet::SkipFromBinary(buffer);
915 }
916 continue;
917 }
918 uint32_t checksum = 0;
919 if (header->SupportMethodChecksum()) {
920 checksum = base::ReadBuffer<uint32_t>(buffer, sizeof(uint32_t));
921 }
922 auto ret = methodInfoMap_.try_emplace(info->GetMethodName(), chunk_);
923 auto methodNameSetIter = ret.first;
924 auto &methodInfo = methodNameSetIter->second.GetOrCreateMethodInfo(checksum, info->GetMethodId());
925 LOG_ECMA(DEBUG) << "Method:" << info->GetMethodId() << ELEMENT_SEPARATOR << info->GetCount()
926 << ELEMENT_SEPARATOR << std::to_string(static_cast<int>(info->GetSampleMode()))
927 << ELEMENT_SEPARATOR << info->GetMethodName();
928 if (header->SupportType()) {
929 methodInfo.GetPGOMethodTypeSet().ParseFromBinary(buffer, header);
930 }
931 }
932
933 return !methodInfoMap_.empty();
934 }
935
GetMismatchResult(const CString & recordName,uint32_t & totalMethodCount,uint32_t & mismatchMethodCount,std::set<std::pair<std::string,CString>> & mismatchMethodSet) const936 void PGOMethodIdSet::GetMismatchResult(const CString &recordName, uint32_t &totalMethodCount,
937 uint32_t &mismatchMethodCount,
938 std::set<std::pair<std::string, CString>> &mismatchMethodSet) const
939 {
940 totalMethodCount += methodInfoMap_.size();
941 for (const auto &methodNameSet : methodInfoMap_) {
942 if (methodNameSet.second.IsMatch()) {
943 continue;
944 }
945 auto info = std::make_pair(methodNameSet.first, recordName);
946 mismatchMethodSet.emplace(info);
947 mismatchMethodCount++;
948 }
949 }
950
Merge(const PGOMethodIdSet & from)951 void PGOMethodIdSet::Merge(const PGOMethodIdSet &from)
952 {
953 for (const auto &methodNameSet : from.methodInfoMap_) {
954 auto iter = methodInfoMap_.find(methodNameSet.first);
955 if (iter == methodInfoMap_.end()) {
956 auto ret = methodInfoMap_.try_emplace(methodNameSet.first, chunk_);
957 iter = ret.first;
958 }
959 const_cast<PGOMethodNameSet &>(iter->second).Merge(methodNameSet.second);
960 }
961 }
962
Merge(const PGODecodeMethodInfo & from)963 void PGODecodeMethodInfo::Merge(const PGODecodeMethodInfo &from)
964 {
965 ASSERT(methodId_.IsValid() && from.methodId_.IsValid());
966 if (!(methodId_ == from.methodId_)) {
967 LOG_ECMA(ERROR) << "MethodId not match. " << methodId_ << " vs " << from.methodId_;
968 return;
969 }
970 pgoMethodTypeSet_.Merge(&from.pgoMethodTypeSet_);
971 }
972
GetMethodInfoMap(const CString & recordName)973 PGOMethodInfoMap *PGORecordDetailInfos::GetMethodInfoMap(const CString &recordName)
974 {
975 auto iter = recordInfos_.find(recordName.c_str());
976 if (iter != recordInfos_.end()) {
977 return iter->second;
978 } else {
979 auto curMethodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
980 recordInfos_.emplace(recordName.c_str(), curMethodInfos);
981 return curMethodInfos;
982 }
983 }
984
AddMethod(const CString & recordName,Method * jsMethod,SampleMode mode,int32_t incCount)985 bool PGORecordDetailInfos::AddMethod(const CString &recordName, Method *jsMethod, SampleMode mode, int32_t incCount)
986 {
987 auto curMethodInfos = GetMethodInfoMap(recordName);
988 ASSERT(curMethodInfos != nullptr);
989 ASSERT(jsMethod != nullptr);
990 return curMethodInfos->AddMethod(chunk_.get(), jsMethod, mode, incCount);
991 }
992
AddType(const CString & recordName,PGOMethodId methodId,int32_t offset,PGOSampleType type)993 bool PGORecordDetailInfos::AddType(const CString &recordName, PGOMethodId methodId, int32_t offset, PGOSampleType type)
994 {
995 auto curMethodInfos = GetMethodInfoMap(recordName);
996 ASSERT(curMethodInfos != nullptr);
997 return curMethodInfos->AddType(chunk_.get(), methodId, offset, type);
998 }
999
AddCallTargetType(const CString & recordName,PGOMethodId methodId,int32_t offset,PGOSampleType type)1000 bool PGORecordDetailInfos::AddCallTargetType(const CString &recordName, PGOMethodId methodId, int32_t offset,
1001 PGOSampleType type)
1002 {
1003 auto curMethodInfos = GetMethodInfoMap(recordName);
1004 ASSERT(curMethodInfos != nullptr);
1005 return curMethodInfos->AddCallTargetType(chunk_.get(), methodId, offset, type);
1006 }
1007
AddObjectInfo(const CString & recordName,EntityId methodId,int32_t offset,const PGOObjectInfo & info)1008 bool PGORecordDetailInfos::AddObjectInfo(
1009 const CString &recordName, EntityId methodId, int32_t offset, const PGOObjectInfo &info)
1010 {
1011 auto curMethodInfos = GetMethodInfoMap(recordName);
1012 ASSERT(curMethodInfos != nullptr);
1013 return curMethodInfos->AddObjectInfo(chunk_.get(), methodId, offset, info);
1014 }
1015
AddDefine(const CString & recordName,PGOMethodId methodId,int32_t offset,PGOSampleType type,PGOSampleType superType)1016 bool PGORecordDetailInfos::AddDefine(
1017 const CString &recordName, PGOMethodId methodId, int32_t offset, PGOSampleType type, PGOSampleType superType)
1018 {
1019 auto curMethodInfos = GetMethodInfoMap(recordName);
1020 ASSERT(curMethodInfos != nullptr);
1021 curMethodInfos->AddDefine(chunk_.get(), methodId, offset, type, superType);
1022
1023 PGOHClassLayoutDesc descInfo(type.GetClassType());
1024 descInfo.SetSuperClassType(superType.GetClassType());
1025 auto iter = moduleLayoutDescInfos_.find(descInfo);
1026 if (iter != moduleLayoutDescInfos_.end()) {
1027 moduleLayoutDescInfos_.erase(iter);
1028 }
1029 moduleLayoutDescInfos_.emplace(descInfo);
1030 return true;
1031 }
1032
AddLayout(PGOSampleType type,JSTaggedType hclass,PGOObjKind kind)1033 bool PGORecordDetailInfos::AddLayout(PGOSampleType type, JSTaggedType hclass, PGOObjKind kind)
1034 {
1035 auto hclassObject = JSHClass::Cast(JSTaggedValue(hclass).GetTaggedObject());
1036 PGOHClassLayoutDesc descInfo(type.GetClassType());
1037 auto iter = moduleLayoutDescInfos_.find(descInfo);
1038 if (iter != moduleLayoutDescInfos_.end()) {
1039 auto &oldDescInfo = const_cast<PGOHClassLayoutDesc &>(*iter);
1040 if (!JSHClass::DumpForProfile(hclassObject, oldDescInfo, kind)) {
1041 return false;
1042 }
1043 } else {
1044 LOG_ECMA(DEBUG) << "The current class did not find a definition";
1045 return false;
1046 }
1047 return true;
1048 }
1049
Merge(const PGORecordDetailInfos & recordInfos)1050 void PGORecordDetailInfos::Merge(const PGORecordDetailInfos &recordInfos)
1051 {
1052 for (auto iter = recordInfos.recordInfos_.begin(); iter != recordInfos.recordInfos_.end(); iter++) {
1053 auto recordName = iter->first;
1054 auto fromMethodInfos = iter->second;
1055
1056 auto recordInfosIter = recordInfos_.find(recordName);
1057 PGOMethodInfoMap *toMethodInfos = nullptr;
1058 if (recordInfosIter == recordInfos_.end()) {
1059 toMethodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
1060 recordInfos_.emplace(recordName, toMethodInfos);
1061 } else {
1062 toMethodInfos = recordInfosIter->second;
1063 }
1064
1065 toMethodInfos->Merge(chunk_.get(), fromMethodInfos);
1066 }
1067 // Merge global layout desc infos to global method info map
1068 for (auto info = recordInfos.moduleLayoutDescInfos_.begin(); info != recordInfos.moduleLayoutDescInfos_.end();
1069 info++) {
1070 auto result = moduleLayoutDescInfos_.find(*info);
1071 if (result == moduleLayoutDescInfos_.end()) {
1072 moduleLayoutDescInfos_.emplace(*info);
1073 } else {
1074 const_cast<PGOHClassLayoutDesc &>(*result).Merge(*info);
1075 }
1076 }
1077 }
1078
ParseFromBinary(void * buffer,PGOProfilerHeader * const header)1079 void PGORecordDetailInfos::ParseFromBinary(void *buffer, PGOProfilerHeader *const header)
1080 {
1081 SectionInfo *info = header->GetRecordInfoSection();
1082 void *addr = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + info->offset_);
1083 for (uint32_t i = 0; i < info->number_; i++) {
1084 auto recordName = base::ReadBuffer(&addr);
1085 PGOMethodInfoMap *methodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
1086 if (methodInfos->ParseFromBinary(chunk_.get(), hotnessThreshold_, &addr, header)) {
1087 recordInfos_.emplace(recordName, methodInfos);
1088 }
1089 }
1090
1091 info = header->GetLayoutDescSection();
1092 if (info == nullptr) {
1093 return;
1094 }
1095 if (header->SupportTrackField()) {
1096 ParseFromBinaryForLayout(&addr, header);
1097 }
1098 }
1099
ParseFromBinaryForLayout(void ** buffer,PGOProfilerHeader * const header)1100 bool PGORecordDetailInfos::ParseFromBinaryForLayout(void **buffer, PGOProfilerHeader *const header)
1101 {
1102 SectionInfo secInfo = base::ReadBuffer<SectionInfo>(buffer);
1103 for (uint32_t i = 0; i < secInfo.number_; i++) {
1104 PGOHClassLayoutDescInner *info = base::ReadBufferInSize<PGOHClassLayoutDescInner>(buffer);
1105 if (info == nullptr) {
1106 LOG_ECMA(INFO) << "Binary format error!";
1107 continue;
1108 }
1109 moduleLayoutDescInfos_.emplace(info->Convert(header));
1110 }
1111 return true;
1112 }
1113
ProcessToBinary(const SaveTask * task,std::fstream & fileStream,PGOProfilerHeader * const header) const1114 void PGORecordDetailInfos::ProcessToBinary(
1115 const SaveTask *task, std::fstream &fileStream, PGOProfilerHeader *const header) const
1116 {
1117 auto info = header->GetRecordInfoSection();
1118 info->number_ = 0;
1119 info->offset_ = static_cast<uint32_t>(fileStream.tellp());
1120 for (auto iter = recordInfos_.begin(); iter != recordInfos_.end(); iter++) {
1121 if (task && task->IsTerminate()) {
1122 LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate";
1123 break;
1124 }
1125 auto recordName = iter->first;
1126 auto curMethodInfos = iter->second;
1127 if (curMethodInfos->ProcessToBinary(hotnessThreshold_, recordName, task, fileStream, header)) {
1128 info->number_++;
1129 }
1130 }
1131 info->size_ = static_cast<uint32_t>(fileStream.tellp()) - info->offset_;
1132
1133 info = header->GetLayoutDescSection();
1134 if (info == nullptr) {
1135 return;
1136 }
1137 info->number_ = 0;
1138 info->offset_ = static_cast<uint32_t>(fileStream.tellp());
1139 if (header->SupportType()) {
1140 if (!ProcessToBinaryForLayout(const_cast<NativeAreaAllocator *>(&nativeAreaAllocator_), task, fileStream)) {
1141 return;
1142 }
1143 info->number_++;
1144 }
1145 info->size_ = static_cast<uint32_t>(fileStream.tellp()) - info->offset_;
1146 header->SetFileSize(static_cast<uint32_t>(fileStream.tellp()));
1147 }
1148
ProcessToBinaryForLayout(NativeAreaAllocator * allocator,const SaveTask * task,std::fstream & stream) const1149 bool PGORecordDetailInfos::ProcessToBinaryForLayout(
1150 NativeAreaAllocator *allocator, const SaveTask *task, std::fstream &stream) const
1151 {
1152 SectionInfo secInfo;
1153 auto layoutBeginPosition = stream.tellp();
1154 stream.seekp(sizeof(SectionInfo), std::ofstream::cur);
1155 for (const auto &typeInfo : moduleLayoutDescInfos_) {
1156 if (task && task->IsTerminate()) {
1157 LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate";
1158 return false;
1159 }
1160 auto classType = PGOSampleType(typeInfo.GetClassType());
1161 auto elementsKind = typeInfo.GetElementsKind();
1162 size_t size = PGOHClassLayoutDescInner::CaculateSize(typeInfo);
1163 if (size == 0) {
1164 continue;
1165 }
1166 auto superType = PGOSampleType(typeInfo.GetSuperClassType());
1167 void *addr = allocator->Allocate(size);
1168 auto descInfos = new (addr) PGOHClassLayoutDescInner(size, classType, superType, elementsKind);
1169 descInfos->Merge(typeInfo);
1170 stream.write(reinterpret_cast<char *>(descInfos), size);
1171 allocator->Delete(addr);
1172 secInfo.number_++;
1173 }
1174
1175 secInfo.offset_ = sizeof(SectionInfo);
1176 secInfo.size_ = static_cast<uint32_t>(stream.tellp()) -
1177 static_cast<uint32_t>(layoutBeginPosition) - sizeof(SectionInfo);
1178 stream.seekp(layoutBeginPosition, std::ofstream::beg)
1179 .write(reinterpret_cast<char *>(&secInfo), sizeof(SectionInfo))
1180 .seekp(0, std::ofstream::end);
1181 return true;
1182 }
1183
ParseFromText(std::ifstream & stream)1184 bool PGORecordDetailInfos::ParseFromText(std::ifstream &stream)
1185 {
1186 std::string details;
1187 while (std::getline(stream, details)) {
1188 if (details.empty()) {
1189 continue;
1190 }
1191 size_t blockIndex = details.find(BLOCK_AND_ARRAY_START);
1192 if (blockIndex == std::string::npos) {
1193 return false;
1194 }
1195 CString recordName = ConvertToString(details.substr(0, blockIndex));
1196
1197 size_t start = details.find_first_of(ARRAY_START);
1198 size_t end = details.find_last_of(ARRAY_END);
1199 if (start == std::string::npos || end == std::string::npos || start > end) {
1200 return false;
1201 }
1202 auto content = details.substr(start + 1, end - (start + 1) - 1);
1203 std::vector<std::string> infoStrings = base::StringHelper::SplitString(content, BLOCK_SEPARATOR);
1204 if (infoStrings.size() <= 0) {
1205 continue;
1206 }
1207
1208 auto methodInfosIter = recordInfos_.find(recordName.c_str());
1209 PGOMethodInfoMap *methodInfos = nullptr;
1210 if (methodInfosIter == recordInfos_.end()) {
1211 methodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
1212 recordInfos_.emplace(recordName.c_str(), methodInfos);
1213 } else {
1214 methodInfos = methodInfosIter->second;
1215 }
1216 if (!methodInfos->ParseFromText(chunk_.get(), hotnessThreshold_, infoStrings)) {
1217 return false;
1218 }
1219 }
1220 return true;
1221 }
1222
ProcessToText(std::ofstream & stream) const1223 void PGORecordDetailInfos::ProcessToText(std::ofstream &stream) const
1224 {
1225 std::string profilerString;
1226 bool isFirst = true;
1227 for (auto layoutInfoIter : moduleLayoutDescInfos_) {
1228 if (isFirst) {
1229 profilerString += NEW_LINE;
1230 profilerString += ARRAY_START + SPACE;
1231 isFirst = false;
1232 } else {
1233 profilerString += BLOCK_SEPARATOR + SPACE;
1234 }
1235 profilerString += PGOHClassLayoutDescInner::GetTypeString(layoutInfoIter);
1236 }
1237 if (!isFirst) {
1238 profilerString += (SPACE + ARRAY_END + NEW_LINE);
1239 stream << profilerString;
1240 }
1241 for (auto iter = recordInfos_.begin(); iter != recordInfos_.end(); iter++) {
1242 auto recordName = iter->first;
1243 auto methodInfos = iter->second;
1244 methodInfos->ProcessToText(hotnessThreshold_, recordName, stream);
1245 }
1246 }
1247
Match(const CString & recordName,EntityId methodId)1248 bool PGORecordSimpleInfos::Match(const CString &recordName, EntityId methodId)
1249 {
1250 auto methodIdsIter = methodIds_.find(recordName);
1251 if (methodIdsIter == methodIds_.end()) {
1252 return false;
1253 }
1254 return methodIdsIter->second->Match(methodId);
1255 }
1256
ParseFromBinary(void * buffer,PGOProfilerHeader * const header)1257 void PGORecordSimpleInfos::ParseFromBinary(void *buffer, PGOProfilerHeader *const header)
1258 {
1259 SectionInfo *info = header->GetRecordInfoSection();
1260 void *addr = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + info->offset_);
1261 for (uint32_t i = 0; i < info->number_; i++) {
1262 auto recordName = base::ReadBuffer(&addr);
1263 PGOMethodIdSet *methodIds = nativeAreaAllocator_.New<PGOMethodIdSet>(chunk_.get());
1264 if (methodIds->ParseFromBinary(hotnessThreshold_, &addr, header)) {
1265 methodIds_.emplace(recordName, methodIds);
1266 }
1267 }
1268
1269 info = header->GetLayoutDescSection();
1270 if (info == nullptr) {
1271 return;
1272 }
1273 if (header->SupportTrackField()) {
1274 ParseFromBinaryForLayout(&addr, header);
1275 }
1276 }
1277
Merge(const PGORecordSimpleInfos & simpleInfos)1278 void PGORecordSimpleInfos::Merge(const PGORecordSimpleInfos &simpleInfos)
1279 {
1280 for (const auto &method : simpleInfos.methodIds_) {
1281 auto result = methodIds_.find(method.first);
1282 if (result == methodIds_.end()) {
1283 PGOMethodIdSet *methodIds = nativeAreaAllocator_.New<PGOMethodIdSet>(chunk_.get());
1284 auto ret = methodIds_.emplace(method.first, methodIds);
1285 ASSERT(ret.second);
1286 result = ret.first;
1287 }
1288 const_cast<PGOMethodIdSet &>(*result->second).Merge(*method.second);
1289 }
1290 // Merge global layout desc infos to global method info map
1291 for (const auto &moduleLayoutDescInfo : simpleInfos.moduleLayoutDescInfos_) {
1292 auto result = moduleLayoutDescInfos_.find(moduleLayoutDescInfo);
1293 if (result == moduleLayoutDescInfos_.end()) {
1294 moduleLayoutDescInfos_.emplace(moduleLayoutDescInfo);
1295 } else {
1296 const_cast<PGOHClassLayoutDesc &>(*result).Merge(moduleLayoutDescInfo);
1297 }
1298 }
1299 }
1300
ParseFromBinaryForLayout(void ** buffer,PGOProfilerHeader * const header)1301 bool PGORecordSimpleInfos::ParseFromBinaryForLayout(void **buffer, PGOProfilerHeader *const header)
1302 {
1303 SectionInfo secInfo = base::ReadBuffer<SectionInfo>(buffer);
1304 for (uint32_t i = 0; i < secInfo.number_; i++) {
1305 PGOHClassLayoutDescInner *info = base::ReadBufferInSize<PGOHClassLayoutDescInner>(buffer);
1306 if (info == nullptr) {
1307 LOG_ECMA(INFO) << "Binary format error!";
1308 continue;
1309 }
1310 moduleLayoutDescInfos_.emplace(info->Convert(header));
1311 }
1312 return true;
1313 }
1314 } // namespace panda::ecmascript
1315