• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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_decoder.h"
17 
18 #include "ecmascript/platform/file.h"
19 
20 namespace panda::ecmascript::pgo {
Load(const std::shared_ptr<PGOAbcFilePool> & externalAbcFilePool)21 bool PGOProfilerDecoder::Load(const std::shared_ptr<PGOAbcFilePool> &externalAbcFilePool)
22 {
23     if (isLoaded_) {
24         Clear();
25     }
26     if (!LoadAPBinaryFile()) {
27         return false;
28     }
29     void *addr = fileMapAddr_.GetOriginAddr();
30 
31     if (!PGOProfilerHeader::ParseFromBinary(addr, fileMapAddr_.GetSize(), &header_)) {
32         UnLoadAPBinaryFile();
33         LOG_PGO(ERROR) << "parse profiler header failed";
34         return false;
35     }
36     pandaFileInfos_.ParseFromBinary(addr, header_->GetPandaInfoSection());
37 
38     if (!recordSimpleInfos_) {
39         recordSimpleInfos_ = std::make_unique<PGORecordSimpleInfos>(hotnessThreshold_);
40     }
41     LoadAbcIdPool(externalAbcFilePool, *recordSimpleInfos_, addr);
42     if (header_->SupportMultiAbcChecksum()) {
43         pandaFileInfos_.UpdateFileInfosAbcID(*recordSimpleInfos_);
44     }
45     recordSimpleInfos_->ParseFromBinary(addr, header_, abcFilePool_);
46 
47     UnLoadAPBinaryFile();
48 
49     isLoaded_ = true;
50     return true;
51 }
52 
Verify(const std::unordered_map<CString,uint32_t> & fileNameToChecksumMap)53 bool PGOProfilerDecoder::Verify(const std::unordered_map<CString, uint32_t>& fileNameToChecksumMap)
54 {
55     if (!isLoaded_) {
56         return false;
57     }
58     // Notice: lx maybe can support method checksum;
59     if (header_->SupportMultiAbcChecksum()) {
60         return pandaFileInfos_.Checksum(fileNameToChecksumMap, abcFilePool_);
61     } else {
62         return pandaFileInfos_.Checksum(fileNameToChecksumMap);
63     }
64 }
65 
LoadAndVerify(const std::unordered_map<CString,uint32_t> & fileNameToChecksumMap,const std::shared_ptr<PGOAbcFilePool> & externalAbcFilePool)66 bool PGOProfilerDecoder::LoadAndVerify(const std::unordered_map<CString, uint32_t>& fileNameToChecksumMap,
67                                        const std::shared_ptr<PGOAbcFilePool>& externalAbcFilePool)
68 {
69     // The file does not exist. Enter full compiler mode.
70     if (inPath_.empty()) {
71         LOG_PGO(INFO) << "file path is empty, enter full compiler mode.";
72         Clear();
73         return true;
74     }
75     if (Load(externalAbcFilePool) && Verify(fileNameToChecksumMap)) {
76         return true;
77     }
78     return false;
79 }
80 
LoadFull(const std::shared_ptr<PGOAbcFilePool> & externalAbcFilePool)81 bool PGOProfilerDecoder::LoadFull(const std::shared_ptr<PGOAbcFilePool> &externalAbcFilePool)
82 {
83     if (isLoaded_) {
84         Clear();
85     }
86     // profiler dump tools may write data to memory when merge ap files.
87     if (!LoadAPBinaryFile(PAGE_PROT_READWRITE)) {
88         return false;
89     }
90     void *addr = fileMapAddr_.GetOriginAddr();
91     size_t fileSize = fileMapAddr_.GetSize();
92     if (!PGOProfilerHeader::ParseFromBinary(addr, fileSize, &header_)) {
93         UnLoadAPBinaryFile();
94         LOG_PGO(ERROR) << "parse profiler header failed";
95         return false;
96     }
97     if (!VerifyAPFile(addr, fileSize)) {
98         UnLoadAPBinaryFile();
99         LOG_PGO(ERROR) << "AP file verification failed";
100         return false;
101     }
102     pandaFileInfos_.ParseFromBinary(addr, header_->GetPandaInfoSection());
103     if (!recordDetailInfos_) {
104         recordDetailInfos_ = std::make_shared<PGORecordDetailInfos>(hotnessThreshold_);
105     }
106     LoadAbcIdPool(externalAbcFilePool, *recordDetailInfos_, addr);
107     if (header_->SupportMultiAbcChecksum()) {
108         pandaFileInfos_.UpdateFileInfosAbcID(*recordDetailInfos_);
109     }
110     if (!recordDetailInfos_->ParseFromBinary(addr, header_, fileSize)) {
111         return false;
112     }
113     recordDetailInfos_->ResetAbcIdRemap();
114     isLoaded_ = true;
115     return true;
116 }
117 
LoadAbcIdPool(const std::shared_ptr<PGOAbcFilePool> & externalAbcFilePool,PGOContext & context,void * addr)118 void PGOProfilerDecoder::LoadAbcIdPool(const std::shared_ptr<PGOAbcFilePool> &externalAbcFilePool,
119                                        PGOContext &context, void *addr)
120 {
121     if (externalAbcFilePool != nullptr) {
122         abcFilePool_ = externalAbcFilePool;
123         externalAbcFilePool_ = true;
124     } else {
125         abcFilePool_ = std::make_unique<PGOAbcFilePool>();
126         externalAbcFilePool_ = false;
127     }
128 
129     if (header_->SupportProfileTypeWithAbcId()) {
130         auto abcFilePoolTemp = std::make_shared<PGOAbcFilePool>();
131         PGOFileSectionInterface::ParseSectionFromBinary(context, addr, header_, *abcFilePoolTemp->GetPool());
132         // step1: [abc pool merge] merge abcFilePool from ap file to memory.
133         abcFilePool_->Merge(context, *abcFilePoolTemp);
134     }
135 }
136 
SaveAPTextFile(const std::string & outPath)137 bool PGOProfilerDecoder::SaveAPTextFile(const std::string &outPath)
138 {
139     if (!isLoaded_) {
140         return false;
141     }
142     std::string realOutPath;
143     if (!RealPath(outPath, realOutPath, false)) {
144         return false;
145     }
146     std::ofstream fileStream(realOutPath.c_str());
147     if (!fileStream.is_open()) {
148         LOG_PGO(ERROR) << "The file path(" << realOutPath << ") open failure!";
149         return false;
150     }
151     if (header_ == nullptr) {
152         LOG_PGO(FATAL) << "PGOProfilerDecoder::SaveAPTextFile:header_ is nullptr";
153     }
154     if (!header_->ProcessToText(fileStream)) {
155         return false;
156     }
157     pandaFileInfos_.ProcessToText(fileStream);
158     recordDetailInfos_->ProcessToText(fileStream);
159     abcFilePool_->GetPool()->ProcessToText(fileStream);
160     return true;
161 }
162 
LoadAPBinaryFile(int prot)163 bool PGOProfilerDecoder::LoadAPBinaryFile(int prot)
164 {
165     std::string realPath;
166     if (!RealPath(inPath_, realPath)) {
167         return false;
168     }
169 
170     static const std::string endString = ".ap";
171     if (realPath.compare(realPath.length() - endString.length(), endString.length(), endString)) {
172         LOG_PGO(ERROR) << "the file path( " << realPath << ") does not end with '.ap'";
173         return false;
174     }
175     LOG_PGO(INFO) << "load profiler from file " << realPath;
176     fileMapAddr_ = FileMap(realPath.c_str(), FILE_RDONLY, prot);
177     if (fileMapAddr_.GetOriginAddr() == nullptr) {
178         LOG_PGO(ERROR) << "file mmap failed";
179         return false;
180     }
181     return true;
182 }
183 
UnLoadAPBinaryFile()184 void PGOProfilerDecoder::UnLoadAPBinaryFile()
185 {
186     if (fileMapAddr_.GetOriginAddr() != nullptr && fileMapAddr_.GetSize() > 0) {
187         FileUnMap(fileMapAddr_);
188         fileMapAddr_.Reset();
189     }
190 }
191 
Clear()192 void PGOProfilerDecoder::Clear()
193 {
194     if (isLoaded_) {
195         UnLoadAPBinaryFile();
196         isVerifySuccess_ = true;
197         hotnessThreshold_ = 0;
198         PGOProfilerHeader::Destroy(&header_);
199         pandaFileInfos_.Clear();
200         if (abcFilePool_ && !externalAbcFilePool_) {
201             abcFilePool_->Clear();
202         }
203         if (recordDetailInfos_) {
204             recordDetailInfos_->Clear();
205         }
206         if (recordSimpleInfos_) {
207             recordSimpleInfos_->Clear();
208         }
209         isLoaded_ = false;
210     }
211 }
212 
Match(const JSPandaFile * jsPandaFile,const CString & recordName,PGOMethodId methodId)213 bool PGOProfilerDecoder::Match(const JSPandaFile *jsPandaFile, const CString &recordName, PGOMethodId methodId)
214 {
215     if (!isLoaded_) {
216         return true;
217     }
218     if (!isVerifySuccess_) {
219         return false;
220     }
221     return recordSimpleInfos_->Match(GetNormalizedFileDesc(jsPandaFile), recordName, EntityId(methodId));
222 }
223 
GetHClassTreeDesc(PGOSampleType profileType,PGOHClassTreeDesc ** desc) const224 bool PGOProfilerDecoder::GetHClassTreeDesc(PGOSampleType profileType, PGOHClassTreeDesc **desc) const
225 {
226     if (!isLoaded_ || !isVerifySuccess_) {
227         return false;
228     }
229     return recordSimpleInfos_->GetHClassTreeDesc(profileType, desc);
230 }
231 
GetMismatchResult(const JSPandaFile * jsPandaFile,uint32_t & totalMethodCount,uint32_t & mismatchMethodCount,std::set<std::pair<std::string,CString>> & mismatchMethodSet) const232 void PGOProfilerDecoder::GetMismatchResult(const JSPandaFile *jsPandaFile, uint32_t &totalMethodCount,
233                                            uint32_t &mismatchMethodCount,
234                                            std::set<std::pair<std::string, CString>> &mismatchMethodSet) const
235 {
236     if (!isLoaded_ || !isVerifySuccess_) {
237         return;
238     }
239     return recordSimpleInfos_->GetMismatchResult(GetNormalizedFileDesc(jsPandaFile), totalMethodCount,
240                                                  mismatchMethodCount, mismatchMethodSet);
241 }
242 
GetNormalizedFileDesc(const JSPandaFile * jsPandaFile) const243 CString PGOProfilerDecoder::GetNormalizedFileDesc(const JSPandaFile *jsPandaFile) const
244 {
245     ASSERT(jsPandaFile != nullptr);
246     if (header_->SupportProfileTypeWithAbcId()) {
247         return jsPandaFile->GetNormalizedFileDesc();
248     }
249     return "";
250 }
251 
InitMergeData()252 bool PGOProfilerDecoder::InitMergeData()
253 {
254     ASSERT(!isLoaded_);
255     if (!recordSimpleInfos_) {
256         recordSimpleInfos_ = std::make_unique<PGORecordSimpleInfos>(hotnessThreshold_);
257     }
258     if (!header_) {
259         // For merge scene, we only care about the ap capability which is in the version field.
260         PGOProfilerHeader::Build(&header_, sizeof(PGOProfilerHeader));
261         ASSERT(header_ != nullptr);
262         memset_s(header_, sizeof(PGOProfilerHeader), 0, sizeof(PGOProfilerHeader));
263     }
264     if (!abcFilePool_) {
265         abcFilePool_ = std::make_shared<PGOAbcFilePool>();
266         externalAbcFilePool_ = false;
267     }
268     isLoaded_ = true;
269     isVerifySuccess_ = true;
270     return true;
271 }
272 
Merge(const PGOProfilerDecoder & decoder)273 void PGOProfilerDecoder::Merge(const PGOProfilerDecoder &decoder)
274 {
275     if (!isLoaded_ || !isVerifySuccess_) {
276         return;
277     }
278     // For merge scene, we chose the highest version from input ap files
279     if (!(header_->CompatibleVerify(decoder.header_->GetVersion()))) {
280         // For merge scene, we only care about the ap capability which is in the version field.
281         memcpy_s(header_, sizeof(base::FileHeaderBase), decoder.header_, sizeof(base::FileHeaderBase));
282     }
283     pandaFileInfos_.Merge(decoder.GetPandaFileInfos());
284     recordSimpleInfos_->Merge(decoder.GetRecordSimpleInfos());
285 }
286 
MergeFileNameToChecksumMap(std::unordered_map<CString,uint32_t> & fileNameToChecksumMap) const287 void PGOProfilerDecoder::MergeFileNameToChecksumMap(std::unordered_map<CString, uint32_t> &fileNameToChecksumMap) const
288 {
289     pandaFileInfos_.ForEachFileInfo([this, &fileNameToChecksumMap](uint32_t checksum, uint32_t abcId) {
290         const CString &abcNameInDecoder = JSPandaFile::GetNormalizedFileDesc(abcFilePool_->GetEntry(abcId)->GetData());
291         fileNameToChecksumMap.emplace(abcNameInDecoder, checksum);
292     });
293 }
294 
VerifyAPFile(void * buffer,size_t bufferSize)295 bool PGOProfilerDecoder::VerifyAPFile(void* buffer, size_t bufferSize)
296 {
297     if (buffer == nullptr || bufferSize == 0) {
298         LOG_PGO(ERROR) << "Invalid buffer parameters";
299         return false;
300     }
301     if (header_ == nullptr) {
302         LOG_PGO(ERROR) << "Header is null during validation";
303         return false;
304     }
305     if (!ValidateSectionBounds(buffer, bufferSize, header_->GetPandaInfoSection(), "PandaInfo")) {
306         return false;
307     }
308     if (!ValidateSectionBounds(buffer, bufferSize, header_->GetRecordInfoSection(), "RecordInfo")) {
309         return false;
310     }
311     if (!ValidateSectionBounds(buffer, bufferSize, header_->GetLayoutDescSection(), "LayoutDesc")) {
312         return false;
313     }
314     if (header_->SupportRecordPool()) {
315         if (!ValidateSectionBounds(buffer, bufferSize, header_->GetRecordPoolSection(), "RecordPool")) {
316             return false;
317         }
318     }
319     if (header_->SupportProtoTransitionPool()) {
320         auto section = header_->GetProtoTransitionPoolSection();
321         if (!ValidateSectionBounds(buffer, bufferSize, section, "ProtoTransitionPool")) {
322             return false;
323         }
324     }
325     if (header_->SupportProfileTypeWithAbcId()) {
326         if (!ValidateSectionBounds(buffer, bufferSize, header_->GetProfileTypeSection(), "ProfileType")) {
327             return false;
328         }
329         if (!ValidateSectionBounds(buffer, bufferSize, header_->GetAbcFilePoolSection(), "AbcFilePool")) {
330             return false;
331         }
332     }
333     LOG_PGO(DEBUG) << "AP file verification passed";
334     return true;
335 }
336 
ValidateSectionBounds(void * buffer,size_t bufferSize,SectionInfo * section,const char * sectionName)337 bool PGOProfilerDecoder::ValidateSectionBounds(void* buffer,
338                                                size_t bufferSize,
339                                                SectionInfo* section,
340                                                const char* sectionName)
341 {
342     if (section == nullptr) {
343         LOG_PGO(ERROR) << "Section " << sectionName << " is null";
344         return false;
345     }
346     uint32_t sectionOffset = section->offset_;
347     uint32_t sectionSize = section->size_;
348     if (sectionOffset >= bufferSize) {
349         LOG_PGO(ERROR) << "Section " << sectionName << " offset (0x" << std::hex << sectionOffset
350                        << ") exceeds buffer size (0x" << std::hex << bufferSize << ")" << std::dec;
351         return false;
352     }
353     if (sectionOffset + sectionSize > bufferSize) {
354         LOG_PGO(ERROR) << "Section " << sectionName << " end (0x" << std::hex << (sectionOffset + sectionSize)
355                        << ") exceeds buffer size (0x" << std::hex << bufferSize << ")" << std::dec;
356         return false;
357     }
358     if (sectionSize > 0 && sectionOffset + sectionSize < sectionOffset) {
359         LOG_PGO(ERROR) << "Section " << sectionName << " size overflow detected";
360         return false;
361     }
362     LOG_PGO(DEBUG) << "Section " << sectionName << " bounds validation passed: offset=0x" << std::hex
363                    << sectionOffset << ", size=0x" << std::hex << sectionSize << std::dec;
364     return true;
365 }
366 } // namespace panda::ecmascript::pgo
367