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
92 if (!PGOProfilerHeader::ParseFromBinary(addr, fileMapAddr_.GetSize(), &header_)) {
93 UnLoadAPBinaryFile();
94 LOG_PGO(ERROR) << "parse profiler header failed";
95 return false;
96 }
97 pandaFileInfos_.ParseFromBinary(addr, header_->GetPandaInfoSection());
98 if (!recordDetailInfos_) {
99 recordDetailInfos_ = std::make_shared<PGORecordDetailInfos>(hotnessThreshold_);
100 }
101
102 LoadAbcIdPool(externalAbcFilePool, *recordDetailInfos_, addr);
103 if (header_->SupportMultiAbcChecksum()) {
104 pandaFileInfos_.UpdateFileInfosAbcID(*recordDetailInfos_);
105 }
106 if (!recordDetailInfos_->ParseFromBinary(addr, header_)) {
107 return false;
108 }
109 recordDetailInfos_->ResetAbcIdRemap();
110 isLoaded_ = true;
111 return true;
112 }
113
LoadAbcIdPool(const std::shared_ptr<PGOAbcFilePool> & externalAbcFilePool,PGOContext & context,void * addr)114 void PGOProfilerDecoder::LoadAbcIdPool(const std::shared_ptr<PGOAbcFilePool> &externalAbcFilePool,
115 PGOContext &context, void *addr)
116 {
117 if (externalAbcFilePool != nullptr) {
118 abcFilePool_ = externalAbcFilePool;
119 externalAbcFilePool_ = true;
120 } else {
121 abcFilePool_ = std::make_unique<PGOAbcFilePool>();
122 externalAbcFilePool_ = false;
123 }
124
125 if (header_->SupportProfileTypeWithAbcId()) {
126 auto abcFilePoolTemp = std::make_shared<PGOAbcFilePool>();
127 PGOFileSectionInterface::ParseSectionFromBinary(context, addr, header_, *abcFilePoolTemp->GetPool());
128 // step1: [abc pool merge] merge abcFilePool from ap file to memory.
129 abcFilePool_->Merge(context, *abcFilePoolTemp);
130 }
131 }
132
SaveAPTextFile(const std::string & outPath)133 bool PGOProfilerDecoder::SaveAPTextFile(const std::string &outPath)
134 {
135 if (!isLoaded_) {
136 return false;
137 }
138 std::string realOutPath;
139 if (!RealPath(outPath, realOutPath, false)) {
140 return false;
141 }
142 std::ofstream fileStream(realOutPath.c_str());
143 if (!fileStream.is_open()) {
144 LOG_PGO(ERROR) << "The file path(" << realOutPath << ") open failure!";
145 return false;
146 }
147 if (header_ == nullptr) {
148 LOG_PGO(FATAL) << "PGOProfilerDecoder::SaveAPTextFile:header_ is nullptr";
149 }
150 if (!header_->ProcessToText(fileStream)) {
151 return false;
152 }
153 pandaFileInfos_.ProcessToText(fileStream);
154 recordDetailInfos_->ProcessToText(fileStream);
155 abcFilePool_->GetPool()->ProcessToText(fileStream);
156 return true;
157 }
158
LoadAPBinaryFile(int prot)159 bool PGOProfilerDecoder::LoadAPBinaryFile(int prot)
160 {
161 std::string realPath;
162 if (!RealPath(inPath_, realPath)) {
163 return false;
164 }
165
166 static const std::string endString = ".ap";
167 if (realPath.compare(realPath.length() - endString.length(), endString.length(), endString)) {
168 LOG_PGO(ERROR) << "the file path( " << realPath << ") does not end with '.ap'";
169 return false;
170 }
171 LOG_PGO(INFO) << "load profiler from file " << realPath;
172 fileMapAddr_ = FileMap(realPath.c_str(), FILE_RDONLY, prot);
173 if (fileMapAddr_.GetOriginAddr() == nullptr) {
174 LOG_PGO(ERROR) << "file mmap failed";
175 return false;
176 }
177 return true;
178 }
179
UnLoadAPBinaryFile()180 void PGOProfilerDecoder::UnLoadAPBinaryFile()
181 {
182 if (fileMapAddr_.GetOriginAddr() != nullptr && fileMapAddr_.GetSize() > 0) {
183 FileUnMap(fileMapAddr_);
184 fileMapAddr_.Reset();
185 }
186 }
187
Clear()188 void PGOProfilerDecoder::Clear()
189 {
190 if (isLoaded_) {
191 UnLoadAPBinaryFile();
192 isVerifySuccess_ = true;
193 hotnessThreshold_ = 0;
194 PGOProfilerHeader::Destroy(&header_);
195 pandaFileInfos_.Clear();
196 if (abcFilePool_ && !externalAbcFilePool_) {
197 abcFilePool_->Clear();
198 }
199 if (recordDetailInfos_) {
200 recordDetailInfos_->Clear();
201 }
202 if (recordSimpleInfos_) {
203 recordSimpleInfos_->Clear();
204 }
205 isLoaded_ = false;
206 }
207 }
208
Match(const JSPandaFile * jsPandaFile,const CString & recordName,PGOMethodId methodId)209 bool PGOProfilerDecoder::Match(const JSPandaFile *jsPandaFile, const CString &recordName, PGOMethodId methodId)
210 {
211 if (!isLoaded_) {
212 return true;
213 }
214 if (!isVerifySuccess_) {
215 return false;
216 }
217 return recordSimpleInfos_->Match(GetNormalizedFileDesc(jsPandaFile), recordName, EntityId(methodId));
218 }
219
GetHClassTreeDesc(PGOSampleType profileType,PGOHClassTreeDesc ** desc) const220 bool PGOProfilerDecoder::GetHClassTreeDesc(PGOSampleType profileType, PGOHClassTreeDesc **desc) const
221 {
222 if (!isLoaded_ || !isVerifySuccess_) {
223 return false;
224 }
225 return recordSimpleInfos_->GetHClassTreeDesc(profileType, desc);
226 }
227
GetMismatchResult(const JSPandaFile * jsPandaFile,uint32_t & totalMethodCount,uint32_t & mismatchMethodCount,std::set<std::pair<std::string,CString>> & mismatchMethodSet) const228 void PGOProfilerDecoder::GetMismatchResult(const JSPandaFile *jsPandaFile, uint32_t &totalMethodCount,
229 uint32_t &mismatchMethodCount,
230 std::set<std::pair<std::string, CString>> &mismatchMethodSet) const
231 {
232 if (!isLoaded_ || !isVerifySuccess_) {
233 return;
234 }
235 return recordSimpleInfos_->GetMismatchResult(GetNormalizedFileDesc(jsPandaFile), totalMethodCount,
236 mismatchMethodCount, mismatchMethodSet);
237 }
238
GetNormalizedFileDesc(const JSPandaFile * jsPandaFile) const239 CString PGOProfilerDecoder::GetNormalizedFileDesc(const JSPandaFile *jsPandaFile) const
240 {
241 ASSERT(jsPandaFile != nullptr);
242 if (header_->SupportProfileTypeWithAbcId()) {
243 return jsPandaFile->GetNormalizedFileDesc();
244 }
245 return "";
246 }
247
InitMergeData()248 bool PGOProfilerDecoder::InitMergeData()
249 {
250 ASSERT(!isLoaded_);
251 if (!recordSimpleInfos_) {
252 recordSimpleInfos_ = std::make_unique<PGORecordSimpleInfos>(hotnessThreshold_);
253 }
254 if (!header_) {
255 // For merge scene, we only care about the ap capability which is in the version field.
256 PGOProfilerHeader::Build(&header_, sizeof(PGOProfilerHeader));
257 ASSERT(header_ != nullptr);
258 memset_s(header_, sizeof(PGOProfilerHeader), 0, sizeof(PGOProfilerHeader));
259 }
260 if (!abcFilePool_) {
261 abcFilePool_ = std::make_shared<PGOAbcFilePool>();
262 externalAbcFilePool_ = false;
263 }
264 isLoaded_ = true;
265 isVerifySuccess_ = true;
266 return true;
267 }
268
Merge(const PGOProfilerDecoder & decoder)269 void PGOProfilerDecoder::Merge(const PGOProfilerDecoder &decoder)
270 {
271 if (!isLoaded_ || !isVerifySuccess_) {
272 return;
273 }
274 // For merge scene, we chose the highest version from input ap files
275 if (!(header_->CompatibleVerify(decoder.header_->GetVersion()))) {
276 // For merge scene, we only care about the ap capability which is in the version field.
277 memcpy_s(header_, sizeof(base::FileHeaderBase), decoder.header_, sizeof(base::FileHeaderBase));
278 }
279 pandaFileInfos_.Merge(decoder.GetPandaFileInfos());
280 recordSimpleInfos_->Merge(decoder.GetRecordSimpleInfos());
281 }
282
MergeFileNameToChecksumMap(std::unordered_map<CString,uint32_t> & fileNameToChecksumMap) const283 void PGOProfilerDecoder::MergeFileNameToChecksumMap(std::unordered_map<CString, uint32_t> &fileNameToChecksumMap) const
284 {
285 pandaFileInfos_.ForEachFileInfo([this, &fileNameToChecksumMap](uint32_t checksum, uint32_t abcId) {
286 const CString &abcNameInDecoder = JSPandaFile::GetNormalizedFileDesc(abcFilePool_->GetEntry(abcId)->GetData());
287 fileNameToChecksumMap.emplace(abcNameInDecoder, checksum);
288 });
289 }
290 } // namespace panda::ecmascript::pgo
291