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