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