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
18 #include "ecmascript/base/bit_helper.h"
19 #include "ecmascript/pgo_profiler/pgo_profiler_saver.h"
20
21 namespace panda::ecmascript {
22 static const std::string ELEMENT_SEPARATOR = "/";
23 static const std::string BLOCK_SEPARATOR = ",";
24 static const std::string BLOCK_START = ":";
25 static const std::string ARRAY_START = "[";
26 static const std::string ARRAY_END = "]";
27 static const std::string NEW_LINE = "\n";
28 static const std::string SPACE = " ";
29 static const std::string BLOCK_AND_ARRAY_START = BLOCK_START + SPACE + ARRAY_START + SPACE;
30 static const std::string VERSION_HEADER = "Profiler Version" + BLOCK_START + SPACE;
31 static const std::string PANDA_FILE_INFO_HEADER = "Panda file sumcheck list" + BLOCK_AND_ARRAY_START;
32
ParseFromBinary(void * buffer,PGOProfilerHeader ** header)33 bool PGOProfilerHeader::ParseFromBinary(void *buffer, PGOProfilerHeader **header)
34 {
35 auto in = reinterpret_cast<PGOProfilerHeader *>(buffer);
36 if (in->Verify()) {
37 size_t desSize = in->Size();
38 if (desSize > LastSize()) {
39 LOG_ECMA(ERROR) << "header size error, expected size is less than " << LastSize()
40 << ", but got " << desSize;
41 return false;
42 }
43 Build(header, desSize);
44 if (memcpy_s(*header, desSize, in, in->Size()) != EOK) {
45 UNREACHABLE();
46 }
47 return true;
48 }
49 return false;
50 }
51
ProcessToBinary(std::ofstream & fileStream) const52 void PGOProfilerHeader::ProcessToBinary(std::ofstream &fileStream) const
53 {
54 fileStream.seekp(0);
55 fileStream.write(reinterpret_cast<const char *>(this), Size());
56 }
57
ParseFromText(std::ifstream & stream)58 bool PGOProfilerHeader::ParseFromText(std::ifstream &stream)
59 {
60 std::string header;
61 if (std::getline(stream, header)) {
62 if (header.empty()) {
63 return false;
64 }
65 auto index = header.find(BLOCK_START);
66 if (index == std::string::npos) {
67 return false;
68 }
69 auto version = header.substr(index + 1);
70 if (!SetVersionInner(version)) {
71 return false;
72 }
73 if (!Verify()) {
74 return false;
75 }
76 return true;
77 }
78 return false;
79 }
80
ProcessToText(std::ofstream & stream) const81 bool PGOProfilerHeader::ProcessToText(std::ofstream &stream) const
82 {
83 if (!Verify()) {
84 return false;
85 }
86 stream << VERSION_HEADER << GetVersionInner() << NEW_LINE;
87 return true;
88 }
89
ParseFromBinary(void * buffer,SectionInfo * const info)90 void PGOPandaFileInfos::ParseFromBinary(void *buffer, SectionInfo *const info)
91 {
92 void *addr = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + info->offset_);
93 for (uint32_t i = 0; i < info->number_; i++) {
94 pandaFileInfos_.emplace(*base::ReadBufferInSize<PandaFileInfo>(&addr));
95 }
96 LOG_ECMA(DEBUG) << "Profiler panda file count:" << info->number_;
97 }
98
ProcessToBinary(std::ofstream & fileStream,SectionInfo * info) const99 void PGOPandaFileInfos::ProcessToBinary(std::ofstream &fileStream, SectionInfo *info) const
100 {
101 fileStream.seekp(info->offset_);
102 info->number_ = pandaFileInfos_.size();
103 for (auto localInfo : pandaFileInfos_) {
104 fileStream.write(reinterpret_cast<char *>(&localInfo), localInfo.Size());
105 }
106 info->size_ = static_cast<uint32_t>(fileStream.tellp()) - info->offset_;
107 }
108
ParseFromText(std::ifstream & stream)109 bool PGOPandaFileInfos::ParseFromText(std::ifstream &stream)
110 {
111 std::string pandaFileInfo;
112 while (std::getline(stream, pandaFileInfo)) {
113 if (pandaFileInfo.empty()) {
114 continue;
115 }
116
117 size_t start = pandaFileInfo.find_first_of(ARRAY_START);
118 size_t end = pandaFileInfo.find_last_of(ARRAY_END);
119 if (start == std::string::npos || end == std::string::npos || start > end) {
120 return false;
121 }
122 auto content = pandaFileInfo.substr(start + 1, end - (start + 1) - 1);
123 std::vector<std::string> infos = base::StringHelper::SplitString(content, BLOCK_SEPARATOR);
124 for (auto checksum : infos) {
125 uint32_t result;
126 if (!base::StringHelper::StrToUInt32(checksum.c_str(), &result)) {
127 LOG_ECMA(ERROR) << "checksum: " << checksum << " parse failed";
128 return false;
129 }
130 Sample(result);
131 }
132 return true;
133 }
134 return true;
135 }
136
ProcessToText(std::ofstream & stream) const137 void PGOPandaFileInfos::ProcessToText(std::ofstream &stream) const
138 {
139 std::string pandaFileInfo = NEW_LINE + PANDA_FILE_INFO_HEADER;
140 bool isFirst = true;
141 for (auto &info : pandaFileInfos_) {
142 if (!isFirst) {
143 pandaFileInfo += BLOCK_SEPARATOR + SPACE;
144 } else {
145 isFirst = false;
146 }
147 pandaFileInfo += std::to_string(info.GetChecksum());
148 }
149
150 pandaFileInfo += (SPACE + ARRAY_END + NEW_LINE);
151 stream << pandaFileInfo;
152 }
153
CheckSum(uint32_t checksum) const154 bool PGOPandaFileInfos::CheckSum(uint32_t checksum) const
155 {
156 if (pandaFileInfos_.find(checksum) == pandaFileInfos_.end()) {
157 LOG_ECMA(ERROR) << "Checksum verification failed. Please ensure that the .abc and .ap match.";
158 return false;
159 }
160 return true;
161 }
162
ProcessToText(std::string & text) const163 void PGOMethodInfo::ProcessToText(std::string &text) const
164 {
165 text += std::to_string(GetMethodId().GetOffset());
166 text += ELEMENT_SEPARATOR;
167 text += std::to_string(GetCount());
168 text += ELEMENT_SEPARATOR;
169 text += GetSampleModeToString();
170 text += ELEMENT_SEPARATOR;
171 text += GetMethodName();
172 }
173
ParseFromText(const std::string & infoString)174 std::vector<std::string> PGOMethodInfo::ParseFromText(const std::string &infoString)
175 {
176 std::vector<std::string> infoStrings = base::StringHelper::SplitString(infoString, ELEMENT_SEPARATOR);
177 return infoStrings;
178 }
179
AddMethod(Chunk * chunk,EntityId methodId,const CString & methodName,SampleMode mode)180 bool PGOMethodInfoMap::AddMethod(Chunk *chunk, EntityId methodId, const CString &methodName, SampleMode mode)
181 {
182 auto result = methodInfos_.find(methodId);
183 if (result != methodInfos_.end()) {
184 auto info = result->second;
185 info->IncreaseCount();
186 info->SetSampleMode(mode);
187 return false;
188 } else {
189 size_t strlen = methodName.size();
190 size_t size = static_cast<size_t>(PGOMethodInfo::Size(strlen));
191 void *infoAddr = chunk->Allocate(size);
192 auto info = new (infoAddr) PGOMethodInfo(methodId, 1, mode, methodName.c_str());
193 methodInfos_.emplace(methodId, info);
194 return true;
195 }
196 }
197
Merge(Chunk * chunk,PGOMethodInfoMap * methodInfos)198 void PGOMethodInfoMap::Merge(Chunk *chunk, PGOMethodInfoMap *methodInfos)
199 {
200 for (auto iter = methodInfos->methodInfos_.begin(); iter != methodInfos->methodInfos_.end(); iter++) {
201 auto methodId = iter->first;
202 auto fromMethodInfo = iter->second;
203
204 auto result = methodInfos_.find(methodId);
205 if (result != methodInfos_.end()) {
206 auto toMethodInfo = result->second;
207 toMethodInfo->Merge(fromMethodInfo);
208 } else {
209 size_t len = strlen(fromMethodInfo->GetMethodName());
210 size_t size = static_cast<size_t>(PGOMethodInfo::Size(len));
211 void *infoAddr = chunk->Allocate(size);
212 auto newMethodInfo = new (infoAddr) PGOMethodInfo(methodId, fromMethodInfo->GetCount(),
213 fromMethodInfo->GetSampleMode(), fromMethodInfo->GetMethodName());
214 methodInfos_.emplace(methodId, newMethodInfo);
215 }
216 fromMethodInfo->ClearCount();
217 }
218 }
219
ParseFromBinary(uint32_t threshold,void ** buffer)220 bool PGOMethodInfoMap::ParseFromBinary(uint32_t threshold, void **buffer)
221 {
222 SectionInfo secInfo = base::ReadBuffer<SectionInfo>(buffer);
223 for (uint32_t j = 0; j < secInfo.number_; j++) {
224 PGOMethodInfo *info = base::ReadBufferInSize<PGOMethodInfo>(buffer);
225 if (!info->IsFilter(threshold)) {
226 methodInfos_.emplace(info->GetMethodId(), info);
227 LOG_ECMA(DEBUG) << "Method:" << info->GetMethodId() << ELEMENT_SEPARATOR << info->GetCount()
228 << ELEMENT_SEPARATOR << std::to_string(static_cast<int>(info->GetSampleMode()))
229 << ELEMENT_SEPARATOR << info->GetMethodName();
230 }
231 }
232 return !methodInfos_.empty();
233 }
234
ProcessToBinary(uint32_t threshold,const CString & recordName,const SaveTask * task,std::ofstream & stream) const235 bool PGOMethodInfoMap::ProcessToBinary(uint32_t threshold, const CString &recordName,
236 const SaveTask *task, std::ofstream &stream) const
237 {
238 SectionInfo secInfo;
239 std::stringstream methodStream;
240 for (auto iter = methodInfos_.begin(); iter != methodInfos_.end(); iter++) {
241 LOG_ECMA(DEBUG) << "Method:" << iter->first << ELEMENT_SEPARATOR << iter->second->GetCount()
242 << ELEMENT_SEPARATOR << std::to_string(static_cast<int>(iter->second->GetSampleMode()))
243 << ELEMENT_SEPARATOR << iter->second->GetMethodName();
244 if (task && task->IsTerminate()) {
245 LOG_ECMA(INFO) << "ProcessProfile: task is already terminate";
246 return false;
247 }
248 auto curMethodInfo = iter->second;
249 if (curMethodInfo->IsFilter(threshold)) {
250 continue;
251 }
252 methodStream.write(reinterpret_cast<char *>(curMethodInfo), curMethodInfo->Size());
253 secInfo.number_++;
254 }
255 if (secInfo.number_ > 0) {
256 secInfo.offset_ = sizeof(SectionInfo);
257 secInfo.size_ = static_cast<uint32_t>(methodStream.tellg());
258 stream << recordName << '\0';
259 stream.write(reinterpret_cast<char *>(&secInfo), sizeof(SectionInfo));
260 stream << methodStream.rdbuf();
261 return true;
262 }
263 return false;
264 }
265
ParseFromText(Chunk * chunk,uint32_t threshold,const std::vector<std::string> & content)266 bool PGOMethodInfoMap::ParseFromText(Chunk *chunk, uint32_t threshold, const std::vector<std::string> &content)
267 {
268 for (auto infoString : content) {
269 std::vector<std::string> infoStrings = PGOMethodInfo::ParseFromText(infoString);
270 if (infoStrings.size() < PGOMethodInfo::METHOD_INFO_COUNT) {
271 LOG_ECMA(ERROR) << "method info:" << infoString << " format error";
272 return false;
273 }
274 uint32_t count;
275 if (!base::StringHelper::StrToUInt32(infoStrings[PGOMethodInfo::METHOD_COUNT_INDEX].c_str(), &count)) {
276 LOG_ECMA(ERROR) << "count: " << infoStrings[PGOMethodInfo::METHOD_COUNT_INDEX] << " parse failed";
277 return false;
278 }
279 SampleMode mode;
280 if (!PGOMethodInfo::GetSampleMode(infoStrings[PGOMethodInfo::METHOD_MODE_INDEX], mode)) {
281 LOG_ECMA(ERROR) << "mode: " << infoStrings[PGOMethodInfo::METHOD_MODE_INDEX] << " parse failed";
282 return false;
283 }
284 if (count < threshold && mode == SampleMode::CALL_MODE) {
285 return true;
286 }
287 uint32_t methodId;
288 if (!base::StringHelper::StrToUInt32(infoStrings[PGOMethodInfo::METHOD_ID_INDEX].c_str(), &methodId)) {
289 LOG_ECMA(ERROR) << "method id: " << infoStrings[PGOMethodInfo::METHOD_ID_INDEX] << " parse failed" ;
290 return false;
291 }
292 std::string methodName = infoStrings[PGOMethodInfo::METHOD_NAME_INDEX];
293
294 size_t len = methodName.size();
295 void *infoAddr = chunk->Allocate(PGOMethodInfo::Size(len));
296 auto info = new (infoAddr) PGOMethodInfo(EntityId(methodId), count, mode, methodName.c_str());
297 methodInfos_.emplace(methodId, info);
298 }
299
300 return true;
301 }
302
ProcessToText(uint32_t threshold,const CString & recordName,std::ofstream & stream) const303 void PGOMethodInfoMap::ProcessToText(uint32_t threshold, const CString &recordName, std::ofstream &stream) const
304 {
305 std::string profilerString;
306 bool isFirst = true;
307 for (auto methodInfoIter : methodInfos_) {
308 auto methodInfo = methodInfoIter.second;
309 if (methodInfo->IsFilter(threshold)) {
310 continue;
311 }
312 if (isFirst) {
313 profilerString += NEW_LINE;
314 profilerString += recordName;
315 profilerString += BLOCK_AND_ARRAY_START;
316 isFirst = false;
317 } else {
318 profilerString += BLOCK_SEPARATOR + SPACE;
319 }
320 methodInfo->ProcessToText(profilerString);
321 }
322 if (!isFirst) {
323 profilerString += (SPACE + ARRAY_END + NEW_LINE);
324 stream << profilerString;
325 }
326 }
327
ParseFromBinary(uint32_t threshold,void ** buffer)328 bool PGOMethodIdSet::ParseFromBinary(uint32_t threshold, void **buffer)
329 {
330 SectionInfo secInfo = base::ReadBuffer<SectionInfo>(buffer);
331 for (uint32_t j = 0; j < secInfo.number_; j++) {
332 PGOMethodInfo *info = base::ReadBufferInSize<PGOMethodInfo>(buffer);
333 if (!info->IsFilter(threshold)) {
334 methodIdSet_.emplace(info->GetMethodId());
335 LOG_ECMA(DEBUG) << "Method:" << info->GetMethodId() << ELEMENT_SEPARATOR << info->GetCount()
336 << ELEMENT_SEPARATOR << std::to_string(static_cast<int>(info->GetSampleMode()))
337 << ELEMENT_SEPARATOR << info->GetMethodName();
338 }
339 }
340
341 return methodIdSet_.size() != 0;
342 }
343
AddMethod(const CString & recordName,EntityId methodId,const CString & methodName,SampleMode mode)344 bool PGORecordDetailInfos::AddMethod(const CString &recordName, EntityId methodId,
345 const CString &methodName, SampleMode mode)
346 {
347 auto iter = recordInfos_.find(recordName.c_str());
348 PGOMethodInfoMap *curMethodInfos = nullptr;
349 if (iter != recordInfos_.end()) {
350 curMethodInfos = iter->second;
351 } else {
352 curMethodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
353 recordInfos_.emplace(recordName.c_str(), curMethodInfos);
354 }
355 return curMethodInfos->AddMethod(chunk_.get(), methodId, methodName, mode);
356 }
357
Merge(const PGORecordDetailInfos & recordInfos)358 void PGORecordDetailInfos::Merge(const PGORecordDetailInfos &recordInfos)
359 {
360 for (auto iter = recordInfos.recordInfos_.begin(); iter != recordInfos.recordInfos_.end(); iter++) {
361 auto recordName = iter->first;
362 auto fromMethodInfos = iter->second;
363
364 auto recordInfosIter = recordInfos_.find(recordName);
365 PGOMethodInfoMap *toMethodInfos = nullptr;
366 if (recordInfosIter == recordInfos_.end()) {
367 toMethodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
368 recordInfos_.emplace(recordName, toMethodInfos);
369 } else {
370 toMethodInfos = recordInfosIter->second;
371 }
372
373 toMethodInfos->Merge(chunk_.get(), fromMethodInfos);
374 }
375 }
376
ParseFromBinary(void * buffer,SectionInfo * const info)377 void PGORecordDetailInfos::ParseFromBinary(void *buffer, SectionInfo *const info)
378 {
379 void *addr = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + info->offset_);
380 for (uint32_t i = 0; i < info->number_; i++) {
381 auto recordName = base::ReadBuffer(&addr);
382 PGOMethodInfoMap *methodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
383 if (methodInfos->ParseFromBinary(hotnessThreshold_, &addr)) {
384 recordInfos_.emplace(recordName, methodInfos);
385 }
386 }
387 }
388
ProcessToBinary(const SaveTask * task,std::ofstream & fileStream,SectionInfo * info) const389 void PGORecordDetailInfos::ProcessToBinary(const SaveTask *task, std::ofstream &fileStream, SectionInfo *info) const
390 {
391 info->number_ = 0;
392 info->offset_ = static_cast<uint32_t>(fileStream.tellp());
393 for (auto iter = recordInfos_.begin(); iter != recordInfos_.end(); iter++) {
394 auto recordName = iter->first;
395 auto curMethodInfos = iter->second;
396 if (curMethodInfos->ProcessToBinary(hotnessThreshold_, recordName, task, fileStream)) {
397 info->number_++;
398 }
399 }
400 info->size_ = static_cast<uint32_t>(fileStream.tellp()) - info->offset_;
401 }
402
ParseFromText(std::ifstream & stream)403 bool PGORecordDetailInfos::ParseFromText(std::ifstream &stream)
404 {
405 std::string details;
406 while (std::getline(stream, details)) {
407 if (details.empty()) {
408 continue;
409 }
410 size_t blockIndex = details.find(BLOCK_AND_ARRAY_START);
411 if (blockIndex == std::string::npos) {
412 return false;
413 }
414 CString recordName = ConvertToString(details.substr(0, blockIndex));
415
416 size_t start = details.find_first_of(ARRAY_START);
417 size_t end = details.find_last_of(ARRAY_END);
418 if (start == std::string::npos || end == std::string::npos || start > end) {
419 return false;
420 }
421 auto content = details.substr(start + 1, end - (start + 1) - 1);
422 std::vector<std::string> infoStrings = base::StringHelper::SplitString(content, BLOCK_SEPARATOR);
423 if (infoStrings.size() <= 0) {
424 continue;
425 }
426
427 auto methodInfosIter = recordInfos_.find(recordName.c_str());
428 PGOMethodInfoMap *methodInfos = nullptr;
429 if (methodInfosIter == recordInfos_.end()) {
430 methodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
431 recordInfos_.emplace(recordName.c_str(), methodInfos);
432 } else {
433 methodInfos = methodInfosIter->second;
434 }
435 if (!methodInfos->ParseFromText(chunk_.get(), hotnessThreshold_, infoStrings)) {
436 return false;
437 }
438 }
439 return true;
440 }
441
ProcessToText(std::ofstream & stream) const442 void PGORecordDetailInfos::ProcessToText(std::ofstream &stream) const
443 {
444 for (auto iter = recordInfos_.begin(); iter != recordInfos_.end(); iter++) {
445 auto recordName = iter->first;
446 auto methodInfos = iter->second;
447 methodInfos->ProcessToText(hotnessThreshold_, recordName, stream);
448 }
449 }
450
Match(const CString & recordName,EntityId methodId)451 bool PGORecordSimpleInfos::Match(const CString &recordName, EntityId methodId)
452 {
453 auto methodIdsIter = methodIds_.find(recordName);
454 if (methodIdsIter == methodIds_.end()) {
455 return false;
456 }
457 return methodIdsIter->second->Match(methodId);
458 }
459
ParseFromBinary(void * buffer,SectionInfo * const info)460 void PGORecordSimpleInfos::ParseFromBinary(void *buffer, SectionInfo *const info)
461 {
462 void *addr = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + info->offset_);
463 for (uint32_t i = 0; i < info->number_; i++) {
464 auto recordName = base::ReadBuffer(&addr);
465 PGOMethodIdSet *methodIds = nativeAreaAllocator_.New<PGOMethodIdSet>();
466 if (methodIds->ParseFromBinary(hotnessThreshold_, &addr)) {
467 methodIds_.emplace(recordName, methodIds);
468 }
469 }
470 }
471 } // namespace panda::ecmascript
472