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