1 /*
2 * Copyright (c) 2023 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 <cerrno>
17 #include <cstdio>
18 #include <fstream>
19 #include <memory>
20 #include <string>
21
22 #include "ecmascript/log_wrapper.h"
23 #include "ecmascript/ohos/enable_aot_list_helper.h"
24 #include "ecmascript/mem/c_string.h"
25 #include "ecmascript/pgo_profiler/ap_file/pgo_file_info.h"
26 #include "ecmascript/pgo_profiler/pgo_profiler_decoder.h"
27 #include "ecmascript/pgo_profiler/pgo_profiler_encoder.h"
28 #include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
29 #include "ecmascript/pgo_profiler/pgo_utils.h"
30 #include "ecmascript/platform/file.h"
31 #include "ecmascript/platform/mutex.h"
32
33 namespace panda::ecmascript::pgo {
Destroy()34 void PGOProfilerEncoder::Destroy()
35 {
36 pandaFileInfos_->Clear();
37 abcFilePool_->Clear();
38 if (!isProfilingInitialized_) {
39 return;
40 }
41 PGOProfilerHeader::Destroy(&header_);
42 globalRecordInfos_->Clear();
43 globalRecordInfos_.reset();
44 isProfilingInitialized_ = false;
45 }
46
ResetOutPathByModuleName(const std::string & moduleName)47 bool PGOProfilerEncoder::ResetOutPathByModuleName(const std::string &moduleName)
48 {
49 LockHolder lock(mutex_);
50 // only first assign takes effect
51 if (!moduleName_.empty() || moduleName.empty()) {
52 return false;
53 }
54 moduleName_ = moduleName;
55 return ResetOutPath(ApNameUtils::GetRuntimeApName(moduleName_));
56 }
57
ResetOutPath(const std::string & profileFileName)58 bool PGOProfilerEncoder::ResetOutPath(const std::string &profileFileName)
59 {
60 if (!RealPath(outDir_, realOutPath_, false)) {
61 return false;
62 }
63
64 auto suffixLength = ApNameUtils::AP_SUFFIX.length();
65 if (realOutPath_.compare(realOutPath_.length() - suffixLength, suffixLength, ApNameUtils::AP_SUFFIX)) {
66 realOutPath_ += "/" + profileFileName;
67 }
68 LOG_ECMA(INFO) << "Save profiler to file:" << realOutPath_;
69 return true;
70 }
71
InitializeData()72 bool PGOProfilerEncoder::InitializeData()
73 {
74 if (!isProfilingInitialized_) {
75 if (!ResetOutPath(ApNameUtils::DEFAULT_AP_NAME)) {
76 return false;
77 }
78 PGOProfilerHeader::Build(&header_, PGOProfilerHeader::LastSize());
79 globalRecordInfos_ = std::make_shared<PGORecordDetailInfos>(hotnessThreshold_);
80 isProfilingInitialized_ = true;
81 }
82 return true;
83 }
84
SamplePandaFileInfo(uint32_t checksum,const CString & abcName)85 void PGOProfilerEncoder::SamplePandaFileInfo(uint32_t checksum, const CString &abcName)
86 {
87 WriteLockHolder lock(rwLock_);
88 pandaFileInfos_->Sample(checksum);
89 ApEntityId entryId(0);
90 abcFilePool_->TryAdd(abcName, entryId);
91 }
92
GetPandaFileId(const CString & abcName,ApEntityId & entryId)93 bool PGOProfilerEncoder::GetPandaFileId(const CString &abcName, ApEntityId &entryId)
94 {
95 ReadLockHolder lock(rwLock_);
96 return abcFilePool_->GetEntryId(abcName, entryId);
97 }
98
GetPandaFileDesc(ApEntityId abcId,CString & desc)99 bool PGOProfilerEncoder::GetPandaFileDesc(ApEntityId abcId, CString &desc)
100 {
101 if (!isProfilingInitialized_) {
102 return false;
103 }
104 ReadLockHolder lock(rwLock_);
105 const auto *entry = abcFilePool_->GetEntry(abcId);
106 if (entry == nullptr) {
107 return false;
108 }
109 desc = entry->GetData();
110 return true;
111 }
112
Merge(const PGORecordDetailInfos & recordInfos)113 void PGOProfilerEncoder::Merge(const PGORecordDetailInfos &recordInfos)
114 {
115 if (!isProfilingInitialized_) {
116 return;
117 }
118 LockHolder lock(mutex_);
119 globalRecordInfos_->Merge(recordInfos);
120 }
121
Merge(const PGOPandaFileInfos & pandaFileInfos)122 void PGOProfilerEncoder::Merge(const PGOPandaFileInfos &pandaFileInfos)
123 {
124 return pandaFileInfos_->Merge(pandaFileInfos);
125 }
126
Merge(const PGOProfilerEncoder & encoder)127 void PGOProfilerEncoder::Merge(const PGOProfilerEncoder &encoder)
128 {
129 Merge(*encoder.pandaFileInfos_);
130 Merge(*encoder.globalRecordInfos_);
131 }
132
VerifyPandaFileMatched(const PGOPandaFileInfos & pandaFileInfos,const std::string & base,const std::string & incoming) const133 bool PGOProfilerEncoder::VerifyPandaFileMatched(const PGOPandaFileInfos &pandaFileInfos, const std::string &base,
134 const std::string &incoming) const
135 {
136 return pandaFileInfos_->VerifyChecksum(pandaFileInfos, base, incoming);
137 }
138
Save()139 bool PGOProfilerEncoder::Save()
140 {
141 if (!isProfilingInitialized_) {
142 return false;
143 }
144 LockHolder lock(mutex_);
145 return InternalSave();
146 }
147
MergeWithExistProfile(PGOProfilerEncoder & runtimeEncoder,PGOProfilerDecoder & decoder,const SaveTask * task)148 void PGOProfilerEncoder::MergeWithExistProfile(PGOProfilerEncoder &runtimeEncoder, PGOProfilerDecoder &decoder,
149 const SaveTask *task)
150 {
151 // inherit some info from runtime encoder
152 ASSERT(header_ != nullptr);
153 ASSERT(runtimeEncoder.header_ != nullptr);
154 header_->SetVersion(runtimeEncoder.header_->GetVersion());
155 bundleName_ = runtimeEncoder.bundleName_;
156 moduleName_ = runtimeEncoder.moduleName_;
157
158 // copy abcFilePool from runtime to temp merger.
159 ASSERT(abcFilePool_->GetPool()->Empty());
160 abcFilePool_->Copy(runtimeEncoder.abcFilePool_);
161 if (!decoder.LoadFull(abcFilePool_)) {
162 LOG_ECMA(ERROR) << "Fail to load ap: " << realOutPath_;
163 } else {
164 Merge(decoder.GetPandaFileInfos());
165 globalRecordInfos_ = decoder.GetRecordDetailInfosPtr();
166 }
167 if (task && task->IsTerminate()) {
168 LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate";
169 return;
170 }
171 Merge(*runtimeEncoder.pandaFileInfos_);
172 if (task && task->IsTerminate()) {
173 LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate";
174 return;
175 }
176 Merge(*runtimeEncoder.globalRecordInfos_);
177 }
178
SaveAndRename(const SaveTask * task)179 bool PGOProfilerEncoder::SaveAndRename(const SaveTask *task)
180 {
181 static const char *tempSuffix = ".tmp";
182 auto tmpOutPath = realOutPath_ + "." + std::to_string(getpid()) + tempSuffix;
183 std::fstream fileStream(tmpOutPath.c_str(),
184 std::fstream::binary | std::fstream::out | std::fstream::in | std::fstream::trunc);
185 if (!fileStream.is_open()) {
186 LOG_ECMA(ERROR) << "The file path(" << tmpOutPath << ") open failure! errno: " << errno;
187 return false;
188 }
189 pandaFileInfos_->ProcessToBinary(fileStream, header_->GetPandaInfoSection());
190 globalRecordInfos_->ProcessToBinary(task, fileStream, header_);
191 {
192 ReadLockHolder lock(rwLock_);
193 PGOFileSectionInterface::ProcessSectionToBinary(*globalRecordInfos_, fileStream, header_,
194 *abcFilePool_->GetPool());
195 }
196 header_->SetFileSize(static_cast<uint32_t>(fileStream.tellp()));
197 header_->ProcessToBinary(fileStream);
198 if (header_->SupportFileConsistency()) {
199 AddChecksum(fileStream);
200 }
201 fileStream.close();
202 if (task && task->IsTerminate()) {
203 LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate";
204 return false;
205 }
206 if (FileExist(realOutPath_.c_str()) && remove(realOutPath_.c_str())) {
207 LOG_ECMA(ERROR) << "Remove " << realOutPath_ << " failure!, errno: " << errno;
208 return false;
209 }
210 if (rename(tmpOutPath.c_str(), realOutPath_.c_str())) {
211 LOG_ECMA(ERROR) << "Rename " << tmpOutPath << " --> " << realOutPath_ << " failure!, errno: " << errno;
212 return false;
213 }
214 RequestAot();
215 return true;
216 }
217
RequestAot()218 void PGOProfilerEncoder::RequestAot()
219 {
220 if (bundleName_.empty() || moduleName_.empty()) {
221 return;
222 }
223
224 LOG_ECMA(INFO) << "Request local aot, bundle: " << bundleName_ << ", module: " << moduleName_;
225 if (!PGOProfilerManager::GetInstance()->RequestAot(bundleName_, moduleName_, RequestAotMode::RE_COMPILE_ON_IDLE)) {
226 LOG_ECMA(ERROR) << "Request aot failed, bundle: " << bundleName_ << ", module: " << moduleName_;
227 }
228 }
229
InternalSave(const SaveTask * task)230 bool PGOProfilerEncoder::InternalSave(const SaveTask *task)
231 {
232 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PGOProfilerEncoder::InternalSave");
233 if (!isProfilingInitialized_) {
234 return false;
235 }
236 if ((mode_ == MERGE) && FileExist(realOutPath_.c_str())) {
237 PGOProfilerEncoder encoder(realOutPath_, hotnessThreshold_, mode_);
238 encoder.InitializeData();
239 PGOProfilerDecoder decoder(realOutPath_, hotnessThreshold_);
240 encoder.MergeWithExistProfile(*this, decoder, task);
241 auto saveAndRenameResult = encoder.SaveAndRename(task);
242 encoder.Destroy();
243 return saveAndRenameResult;
244 }
245 return SaveAndRename(task);
246 }
247
AddChecksum(std::fstream & fileStream)248 void PGOProfilerEncoder::AddChecksum(std::fstream &fileStream)
249 {
250 static constexpr uint32_t KILO_BYTES = 1024;
251 static constexpr uint32_t STEP_IN_KB = 256;
252 static constexpr uint32_t STEP_SIZE = STEP_IN_KB * KILO_BYTES;
253 uint32_t size = static_cast<uint32_t>(fileStream.seekp(0, std::fstream::end).tellp());
254 std::unique_ptr<std::vector<uint8_t>> buffer = std::make_unique<std::vector<uint8_t>>(STEP_SIZE);
255 // first, calculate the version field's checksum.
256 fileStream.seekg(PGOProfilerHeader::MAGIC_SIZE, std::fstream::beg)
257 .read(reinterpret_cast<char *>(buffer->data()), PGOProfilerHeader::VERSION_SIZE);
258 uint32_t checksum = adler32(0, reinterpret_cast<const Bytef *>(buffer->data()), PGOProfilerHeader::VERSION_SIZE);
259 // second, calculate the checksum for remaining content(exclude checksum field).
260 uint32_t remainingSize = size - PGOProfilerHeader::CHECKSUM_END_OFFSET;
261 fileStream.seekg(PGOProfilerHeader::CHECKSUM_END_OFFSET);
262 while (remainingSize > 0) {
263 uint32_t readSize = std::min(STEP_SIZE, remainingSize);
264 remainingSize = remainingSize - readSize;
265 fileStream.read(reinterpret_cast<char *>(buffer->data()), readSize);
266 checksum = adler32(checksum, reinterpret_cast<const Bytef *>(buffer->data()), readSize);
267 }
268 // third, write the checksum back to the checksum field in the output stream.
269 fileStream.seekp(PGOProfilerHeader::MAGIC_SIZE + PGOProfilerHeader::VERSION_SIZE, std::fstream::beg);
270 fileStream.write(reinterpret_cast<char *>(&checksum), sizeof(checksum));
271 }
272
TerminateSaveTask()273 void PGOProfilerEncoder::TerminateSaveTask()
274 {
275 if (!isProfilingInitialized_) {
276 return;
277 }
278 Taskpool::GetCurrentTaskpool()->TerminateTask(GLOBAL_TASK_ID, TaskType::PGO_SAVE_TASK);
279 }
280
PostSaveTask()281 void PGOProfilerEncoder::PostSaveTask()
282 {
283 if (!isProfilingInitialized_) {
284 return;
285 }
286 Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<SaveTask>(this, GLOBAL_TASK_ID));
287 }
288
PostResetOutPathTask(const std::string & moduleName)289 void PGOProfilerEncoder::PostResetOutPathTask(const std::string &moduleName)
290 {
291 if (moduleName.empty()) {
292 LOG_ECMA(ERROR) << "PostSetModuleNameTask: moduleName is empty.";
293 return;
294 }
295 // only post moduleName once
296 bool hasPost = false;
297 if (!hasPostModuleName_.compare_exchange_strong(hasPost, true)) {
298 return;
299 }
300 Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<ResetOutPathTask>(this, moduleName, GLOBAL_TASK_ID));
301 }
302
StartSaveTask(const SaveTask * task)303 void PGOProfilerEncoder::StartSaveTask(const SaveTask *task)
304 {
305 if (task == nullptr) {
306 return;
307 }
308 if (task->IsTerminate()) {
309 LOG_ECMA(ERROR) << "StartSaveTask: task is already terminate";
310 return;
311 }
312 LockHolder lock(mutex_);
313 InternalSave(task);
314 }
315
LoadAPTextFile(const std::string & inPath)316 bool PGOProfilerEncoder::LoadAPTextFile(const std::string &inPath)
317 {
318 if (!isProfilingInitialized_) {
319 return false;
320 }
321 std::string realPath;
322 if (!RealPath(inPath, realPath)) {
323 return false;
324 }
325
326 std::ifstream fileStream(realPath.c_str());
327 if (!fileStream.is_open()) {
328 LOG_ECMA(ERROR) << "The file path(" << realOutPath_ << ") open failure!";
329 return false;
330 }
331
332 if (!header_->ParseFromText(fileStream)) {
333 LOG_ECMA(ERROR) << "header format error";
334 return false;
335 }
336 if (!pandaFileInfos_->ParseFromText(fileStream)) {
337 LOG_ECMA(ERROR) << "panda file info format error";
338 return false;
339 }
340 if (!globalRecordInfos_->ParseFromText(fileStream)) {
341 LOG_ECMA(ERROR) << "record info format error";
342 return false;
343 }
344
345 return true;
346 }
347 } // namespace panda::ecmascript::pgo
348