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 #include <memory>
18
19 #include "ecmascript/platform/file.h"
20 #include "ecmascript/pgo_profiler/pgo_profiler_info.h"
21
22 namespace panda::ecmascript::pgo {
Load(const std::shared_ptr<PGOAbcFilePool> & externalAbcFilePool)23 bool PGOProfilerDecoder::Load(const std::shared_ptr<PGOAbcFilePool> &externalAbcFilePool)
24 {
25 if (isLoaded_) {
26 Clear();
27 }
28 if (!LoadAPBinaryFile()) {
29 return false;
30 }
31 void *addr = fileMapAddr_.GetOriginAddr();
32
33 if (!PGOProfilerHeader::ParseFromBinary(addr, fileMapAddr_.GetSize(), &header_)) {
34 UnLoadAPBinaryFile();
35 LOG_ECMA(ERROR) << "Parse profiler header failed";
36 return false;
37 }
38 pandaFileInfos_.ParseFromBinary(addr, header_->GetPandaInfoSection());
39
40 if (!recordSimpleInfos_) {
41 recordSimpleInfos_ = std::make_unique<PGORecordSimpleInfos>(hotnessThreshold_);
42 }
43 LoadAbcIdPool(externalAbcFilePool, *recordSimpleInfos_, addr);
44 recordSimpleInfos_->ParseFromBinary(addr, header_, abcFilePool_);
45 UnLoadAPBinaryFile();
46
47 isLoaded_ = true;
48 return true;
49 }
50
Verify(uint32_t checksum)51 bool PGOProfilerDecoder::Verify(uint32_t checksum)
52 {
53 if (!isLoaded_) {
54 return false;
55 }
56 // Notice: lx maybe can support method checksum;
57 return pandaFileInfos_.Checksum(checksum);
58 }
59
LoadAndVerify(uint32_t checksum,const std::shared_ptr<PGOAbcFilePool> & externalAbcFilePool)60 bool PGOProfilerDecoder::LoadAndVerify(uint32_t checksum, const std::shared_ptr<PGOAbcFilePool> &externalAbcFilePool)
61 {
62 // The file does not exist. Enter full compiler mode.
63 if (inPath_.empty()) {
64 LOG_ECMA(INFO) << "When the file is empty. Enter full compiler mode.";
65 Clear();
66 return true;
67 }
68 if (Load(externalAbcFilePool) && Verify(checksum)) {
69 return true;
70 }
71 return false;
72 }
73
LoadFull(const std::shared_ptr<PGOAbcFilePool> & externalAbcFilePool)74 bool PGOProfilerDecoder::LoadFull(const std::shared_ptr<PGOAbcFilePool> &externalAbcFilePool)
75 {
76 if (isLoaded_) {
77 Clear();
78 }
79 // profiler dump tools may write data to memory when merge ap files.
80 if (!LoadAPBinaryFile(PAGE_PROT_READWRITE)) {
81 return false;
82 }
83 void *addr = fileMapAddr_.GetOriginAddr();
84
85 if (!PGOProfilerHeader::ParseFromBinary(addr, fileMapAddr_.GetSize(), &header_)) {
86 UnLoadAPBinaryFile();
87 LOG_ECMA(ERROR) << "Parse profiler header failed";
88 return false;
89 }
90 pandaFileInfos_.ParseFromBinary(addr, header_->GetPandaInfoSection());
91 if (!recordDetailInfos_) {
92 recordDetailInfos_ = std::make_shared<PGORecordDetailInfos>(hotnessThreshold_);
93 }
94
95 LoadAbcIdPool(externalAbcFilePool, *recordDetailInfos_, addr);
96 if (!recordDetailInfos_->ParseFromBinary(addr, header_)) {
97 return false;
98 }
99 recordDetailInfos_->ResetAbcIdRemap();
100 isLoaded_ = true;
101 return true;
102 }
103
LoadAbcIdPool(const std::shared_ptr<PGOAbcFilePool> & externalAbcFilePool,PGOContext & context,void * addr)104 void PGOProfilerDecoder::LoadAbcIdPool(const std::shared_ptr<PGOAbcFilePool> &externalAbcFilePool,
105 PGOContext &context, void *addr)
106 {
107 if (externalAbcFilePool != nullptr) {
108 abcFilePool_ = externalAbcFilePool;
109 externalAbcFilePool_ = true;
110 } else {
111 abcFilePool_ = std::make_unique<PGOAbcFilePool>();
112 externalAbcFilePool_ = false;
113 }
114
115 if (header_->SupportProfileTypeWithAbcId()) {
116 auto abcFilePoolTemp = std::make_shared<PGOAbcFilePool>();
117 PGOFileSectionInterface::ParseSectionFromBinary(context, addr, header_, *abcFilePoolTemp->GetPool());
118 // step1: [abc pool merge] merge abcFilePool from ap file to memory.
119 abcFilePool_->Merge(context, *abcFilePoolTemp);
120 }
121 }
122
SaveAPTextFile(const std::string & outPath)123 bool PGOProfilerDecoder::SaveAPTextFile(const std::string &outPath)
124 {
125 if (!isLoaded_) {
126 return false;
127 }
128 std::string realOutPath;
129 if (!RealPath(outPath, realOutPath, false)) {
130 return false;
131 }
132 std::ofstream fileStream(realOutPath.c_str());
133 if (!fileStream.is_open()) {
134 LOG_ECMA(ERROR) << "The file path(" << realOutPath << ") open failure!";
135 return false;
136 }
137 if (header_ == nullptr) {
138 LOG_ECMA(FATAL) << "PGOProfilerDecoder::SaveAPTextFile:header_ is nullptr";
139 }
140 if (!header_->ProcessToText(fileStream)) {
141 return false;
142 }
143 pandaFileInfos_.ProcessToText(fileStream);
144 recordDetailInfos_->ProcessToText(fileStream);
145 abcFilePool_->GetPool()->ProcessToText(fileStream);
146 return true;
147 }
148
LoadAPBinaryFile(int prot)149 bool PGOProfilerDecoder::LoadAPBinaryFile(int prot)
150 {
151 std::string realPath;
152 if (!RealPath(inPath_, realPath)) {
153 return false;
154 }
155
156 static const std::string endString = ".ap";
157 if (realPath.compare(realPath.length() - endString.length(), endString.length(), endString)) {
158 LOG_ECMA(ERROR) << "The file path( " << realPath << ") does not end with .ap";
159 return false;
160 }
161 LOG_ECMA(INFO) << "Load profiler from file:" << realPath;
162 fileMapAddr_ = FileMap(realPath.c_str(), FILE_RDONLY, prot);
163 if (fileMapAddr_.GetOriginAddr() == nullptr) {
164 LOG_ECMA(ERROR) << "File mmap failed";
165 return false;
166 }
167 return true;
168 }
169
UnLoadAPBinaryFile()170 void PGOProfilerDecoder::UnLoadAPBinaryFile()
171 {
172 if (fileMapAddr_.GetOriginAddr() != nullptr && fileMapAddr_.GetSize() > 0) {
173 FileUnMap(fileMapAddr_);
174 fileMapAddr_.Reset();
175 }
176 }
177
Clear()178 void PGOProfilerDecoder::Clear()
179 {
180 if (isLoaded_) {
181 UnLoadAPBinaryFile();
182 isVerifySuccess_ = true;
183 hotnessThreshold_ = 0;
184 PGOProfilerHeader::Destroy(&header_);
185 pandaFileInfos_.Clear();
186 if (abcFilePool_ && !externalAbcFilePool_) {
187 abcFilePool_->Clear();
188 }
189 if (recordDetailInfos_) {
190 recordDetailInfos_->Clear();
191 }
192 if (recordSimpleInfos_) {
193 recordSimpleInfos_->Clear();
194 }
195 isLoaded_ = false;
196 }
197 }
198
Match(const JSPandaFile * jsPandaFile,const CString & recordName,PGOMethodId methodId)199 bool PGOProfilerDecoder::Match(const JSPandaFile *jsPandaFile, const CString &recordName, PGOMethodId methodId)
200 {
201 if (!isLoaded_) {
202 return true;
203 }
204 if (!isVerifySuccess_) {
205 return false;
206 }
207 return recordSimpleInfos_->Match(GetNormalizedFileDesc(jsPandaFile), recordName, EntityId(methodId));
208 }
209
GetHClassTreeDesc(PGOSampleType profileType,PGOHClassTreeDesc ** desc) const210 bool PGOProfilerDecoder::GetHClassTreeDesc(PGOSampleType profileType, PGOHClassTreeDesc **desc) const
211 {
212 if (!isLoaded_ || !isVerifySuccess_) {
213 return false;
214 }
215 return recordSimpleInfos_->GetHClassTreeDesc(profileType, desc);
216 }
217
GetMismatchResult(const JSPandaFile * jsPandaFile,uint32_t & totalMethodCount,uint32_t & mismatchMethodCount,std::set<std::pair<std::string,CString>> & mismatchMethodSet) const218 void PGOProfilerDecoder::GetMismatchResult(const JSPandaFile *jsPandaFile, uint32_t &totalMethodCount,
219 uint32_t &mismatchMethodCount,
220 std::set<std::pair<std::string, CString>> &mismatchMethodSet) const
221 {
222 if (!isLoaded_ || !isVerifySuccess_) {
223 return;
224 }
225 return recordSimpleInfos_->GetMismatchResult(GetNormalizedFileDesc(jsPandaFile), totalMethodCount,
226 mismatchMethodCount, mismatchMethodSet);
227 }
228
GetNormalizedFileDesc(const JSPandaFile * jsPandaFile) const229 CString PGOProfilerDecoder::GetNormalizedFileDesc(const JSPandaFile *jsPandaFile) const
230 {
231 ASSERT(jsPandaFile != nullptr);
232 if (header_->SupportProfileTypeWithAbcId()) {
233 return jsPandaFile->GetNormalizedFileDesc();
234 }
235 return "";
236 }
237
InitMergeData()238 bool PGOProfilerDecoder::InitMergeData()
239 {
240 ASSERT(!isLoaded_);
241 if (!recordSimpleInfos_) {
242 recordSimpleInfos_ = std::make_unique<PGORecordSimpleInfos>(hotnessThreshold_);
243 }
244 if (!header_) {
245 // For merge scene, we only care about the ap capability which is in the version field.
246 PGOProfilerHeader::Build(&header_, sizeof(PGOProfilerHeader));
247 ASSERT(header_ != nullptr);
248 memset_s(header_, sizeof(PGOProfilerHeader), 0, sizeof(PGOProfilerHeader));
249 }
250 if (!abcFilePool_) {
251 abcFilePool_ = std::make_shared<PGOAbcFilePool>();
252 externalAbcFilePool_ = false;
253 }
254 isLoaded_ = true;
255 isVerifySuccess_ = true;
256 return true;
257 }
258
Merge(const PGOProfilerDecoder & decoder)259 void PGOProfilerDecoder::Merge(const PGOProfilerDecoder &decoder)
260 {
261 if (!isLoaded_ || !isVerifySuccess_) {
262 return;
263 }
264 // For merge scene, we chose the highest version from input ap files
265 if (!(header_->CompatibleVerify(decoder.header_->GetVersion()))) {
266 // For merge scene, we only care about the ap capability which is in the version field.
267 memcpy_s(header_, sizeof(base::FileHeaderBase), decoder.header_, sizeof(base::FileHeaderBase));
268 }
269 pandaFileInfos_.Merge(decoder.GetPandaFileInfos());
270 recordSimpleInfos_->Merge(decoder.GetRecordSimpleInfos());
271 }
272 } // namespace panda::ecmascript::pgo
273