• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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