• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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