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