• 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 
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