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 "ecmascript/pgo_profiler/pgo_profiler_manager.h"
17
18 #include "ecmascript/platform/file.h"
19 #include "ecmascript/platform/os.h"
20
21 namespace panda::ecmascript::pgo {
22 namespace {
23 constexpr int32_t PGO_SAVING_SIGNAL = 50;
24 } // namespace
25
GetInstance()26 PGOProfilerManager* PGOProfilerManager::GetInstance()
27 {
28 static PGOProfilerManager* instance = new PGOProfilerManager();
29 return instance;
30 }
31
MergeApFiles(const std::string & inFiles,const std::string & outPath,uint32_t hotnessThreshold,ApGenMode mode)32 bool PGOProfilerManager::MergeApFiles(const std::string& inFiles,
33 const std::string& outPath,
34 uint32_t hotnessThreshold,
35 ApGenMode mode)
36 {
37 std::string realPath;
38 if (!PGOProfilerManager::ResetOutPath(outPath, realPath, ApNameUtils::DEFAULT_AP_NAME)) {
39 LOG_ECMA(ERROR) << "reset out path failed, outPath: " << outPath
40 << " ,hotnessThreshold: " << hotnessThreshold;
41 return false;
42 }
43 arg_list_t apFileNames = base::StringHelper::SplitString(inFiles, GetFileDelimiter());
44 auto info = std::make_shared<PGOInfo>(hotnessThreshold);
45 bool hasMerged = false;
46 std::string firstApFileName;
47 for (const auto& fileName: apFileNames) {
48 if (!base::StringHelper::EndsWith(fileName, ".ap")) {
49 LOG_ECMA(ERROR) << "The file path (" << fileName << ") does not end with .ap";
50 continue;
51 }
52 PGOProfilerDecoder decoder(fileName, hotnessThreshold);
53 if (!decoder.LoadFull(info->GetAbcFilePoolPtr())) {
54 LOG_ECMA(ERROR) << "Fail to load file path (" << fileName << "), skip it.";
55 continue;
56 }
57 if (!hasMerged) {
58 firstApFileName = fileName;
59 } else {
60 if (!info->VerifyPandaFileMatched(decoder.GetPandaFileInfos(), firstApFileName, fileName)) {
61 continue;
62 }
63 }
64 if (!decoder.IsCompatibleWithAOTFile()) {
65 LOG_ECMA(ERROR) << "The ap file (" << fileName << ") is not compatible with AOT version. skip it";
66 continue;
67 }
68 info->MergeSafe(decoder.GetRecordDetailInfos());
69 info->MergeSafe(decoder.GetPandaFileInfos());
70 hasMerged = true;
71 }
72 if (!hasMerged) {
73 LOG_ECMA(ERROR)
74 << "No ap file pass verify, no ap file compatible an version, no ap file processed. Input files: "
75 << inFiles;
76 GetInstance()->SetIsApFileCompatible(false);
77 return false;
78 }
79 GetInstance()->SetIsApFileCompatible(true);
80 PGOProfilerEncoder encoder(outPath, mode);
81 encoder.Save(info);
82 return true;
83 }
84
MergeApFiles(std::unordered_map<CString,uint32_t> & fileNameToChecksumMap,PGOProfilerDecoder & merger)85 bool PGOProfilerManager::MergeApFiles(std::unordered_map<CString, uint32_t> &fileNameToChecksumMap,
86 PGOProfilerDecoder &merger)
87 {
88 uint32_t hotnessThreshold = merger.GetHotnessThreshold();
89 std::string inFiles(merger.GetInPath());
90 arg_list_t pgoFileNamesVector = base::StringHelper::SplitString(inFiles, GetFileDelimiter());
91 if (pgoFileNamesVector.empty()) {
92 return true;
93 }
94 merger.InitMergeData();
95 bool hasMerged = false;
96 std::string firstApFileName;
97 for (const auto& fileName : pgoFileNamesVector) {
98 PGOProfilerDecoder decoder(fileName, hotnessThreshold);
99 if (!decoder.LoadAndVerify(fileNameToChecksumMap, merger.GetAbcFilePool())) {
100 LOG_ECMA(ERROR) << "Load and verify file (" << fileName << ") failed, skip it.";
101 continue;
102 }
103 if (!hasMerged) {
104 firstApFileName = fileName;
105 } else {
106 if (!merger.GetPandaFileInfos().VerifyChecksum(decoder.GetPandaFileInfos(), firstApFileName, fileName)) {
107 continue;
108 }
109 }
110 if (!decoder.IsCompatibleWithAOTFile()) {
111 LOG_ECMA(ERROR) << "The ap file (" << fileName << ") is not compatible with AOT version. skip it";
112 continue;
113 }
114 merger.Merge(decoder);
115 hasMerged = true;
116 }
117 if (!hasMerged) {
118 LOG_ECMA(ERROR)
119 << "No ap file pass verify, no ap file compatible an version, no ap file processed. Input files: "
120 << inFiles;
121 GetInstance()->SetIsApFileCompatible(false);
122 return false;
123 }
124 GetInstance()->SetIsApFileCompatible(true);
125 // In the scenario of on-device application compilation, the input fileNameToChecksumMap only contains ABC
126 // information of the application, while AP holds all ABC information collected during the
127 // collection phase. Considering external HSP dependencies, the input fileNameToChecksumMap
128 // needs to be merged with the information from AP, that we can write full ABC information to the an file.
129 merger.MergeFileNameToChecksumMap(fileNameToChecksumMap);
130 return true;
131 }
132
RegisterSavingSignal()133 void PGOProfilerManager::RegisterSavingSignal()
134 {
135 if (!isInitialized_) {
136 LOG_PGO(ERROR) << "can not register pgo saving signal, data is not initialized";
137 return;
138 }
139 signal(PGO_SAVING_SIGNAL, SavingSignalHandler);
140 enableSignalSaving_ = true;
141 LOG_PGO(INFO) << "PGO force save signal has been registered";
142 }
143
SavingSignalHandler(int signo)144 void PGOProfilerManager::SavingSignalHandler(int signo)
145 {
146 if (signo != PGO_SAVING_SIGNAL) {
147 return;
148 }
149
150 PGOProfilerManager::GetInstance()->SetForceDump(true);
151 }
152
Initialize(const std::string & outDir,uint32_t hotnessThreshold)153 void PGOProfilerManager::Initialize(const std::string& outDir, uint32_t hotnessThreshold)
154 {
155 outDir_ = outDir;
156 hotnessThreshold_ = hotnessThreshold;
157 pgoInfo_ = std::make_shared<PGOInfo>(hotnessThreshold);
158 LOG_PGO(INFO) << "pgo profiler manager initialized, output directory: " << outDir
159 << ", hotness threshold: " << hotnessThreshold;
160 }
161
SetBundleName(const std::string & bundleName)162 void PGOProfilerManager::SetBundleName(const std::string& bundleName)
163 {
164 bundleName_ = bundleName;
165 }
166
GetBundleName() const167 const std::string PGOProfilerManager::GetBundleName() const
168 {
169 return bundleName_;
170 }
171
SetRequestAotCallback(const RequestAotCallback & cb)172 void PGOProfilerManager::SetRequestAotCallback(const RequestAotCallback& cb)
173 {
174 LockHolder lock(requestAotCallbackMutex_);
175 if (requestAotCallback_ != nullptr) {
176 return;
177 }
178 requestAotCallback_ = cb;
179 }
180
RequestAot(const std::string & bundleName,const std::string & moduleName,RequestAotMode triggerMode)181 bool PGOProfilerManager::RequestAot(const std::string& bundleName,
182 const std::string& moduleName,
183 RequestAotMode triggerMode)
184 {
185 RequestAotCallback cb;
186 {
187 LockHolder lock(requestAotCallbackMutex_);
188 if (requestAotCallback_ == nullptr) {
189 LOG_PGO(ERROR) << "trigger aot failed, callback is null";
190 return false;
191 }
192 cb = requestAotCallback_;
193 }
194 return (cb(bundleName, moduleName, static_cast<int32_t>(triggerMode)) == 0);
195 }
196
Destroy()197 void PGOProfilerManager::Destroy()
198 {
199 LOG_PGO(INFO) << "attempting to destroy PGO profiler manager, PGO profiler data is "
200 << (isInitialized_ ? "initialized" : "not initialized");
201 if (isInitialized_) {
202 SavePGOInfo();
203 {
204 LockHolder lock(GetPGOInfoMutex());
205 pgoInfo_->Clear();
206 pgoInfo_.reset();
207 }
208 isInitialized_ = false;
209 apGenMode_ = ApGenMode::MERGE;
210 outPath_ = "";
211 LOG_PGO(INFO) << "pgo profiler manager destroied";
212 }
213 }
214
BuildProfiler(EcmaVM * vm,bool isEnable)215 std::shared_ptr<PGOProfiler> PGOProfilerManager::BuildProfiler(EcmaVM* vm, bool isEnable)
216 {
217 LOG_PGO(INFO) << "build profiler, pgo is " << (isEnable ? "enabled" : "disabled");
218 if (isEnable) {
219 isEnable = InitializeData();
220 }
221 auto profiler = std::make_shared<PGOProfiler>(vm, isEnable);
222 {
223 LockHolder lock(profilersMutex_);
224 profilers_.insert(profiler);
225 }
226 return profiler;
227 }
228
IsEnable() const229 bool PGOProfilerManager::IsEnable() const
230 {
231 return !disablePGO_ && isInitialized_;
232 }
233
InitializeData()234 bool PGOProfilerManager::InitializeData()
235 {
236 if (isInitialized_) {
237 LOG_PGO(INFO) << "pgo profiler data is already initialized";
238 return true;
239 }
240 if (!pgoInfo_) {
241 LOG_PGO(ERROR) << "pgo profiler data is not initialized properly";
242 return false;
243 }
244 if (!ResetOutPath(ApNameUtils::DEFAULT_AP_NAME)) {
245 LOG_PGO(ERROR) << "failed to reset ap file out path, output directory: " << outDir_;
246 return false;
247 }
248 isInitialized_ = true;
249 if (!enableSignalSaving_) {
250 RegisterSavingSignal();
251 }
252 LOG_PGO(INFO) << "pgo profiler data is initialized";
253 return true;
254 }
255
Destroy(std::shared_ptr<PGOProfiler> & profiler)256 void PGOProfilerManager::Destroy(std::shared_ptr<PGOProfiler>& profiler)
257 {
258 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PGOProfilerManager::Destroy");
259 LOG_PGO(INFO) << "attempting to destroy pgo profiler: " << profiler;
260 if (profiler != nullptr) {
261 pendingProfilers_.Remove(profiler.get());
262 {
263 LockHolder lock(profilersMutex_);
264 profilers_.erase(profiler);
265 }
266 profiler->DumpBeforeDestroy();
267 profiler.reset();
268 LOG_PGO(INFO) << "pgo profiler destroyed";
269 }
270 }
271
Reset(const std::shared_ptr<PGOProfiler> & profiler,bool isEnable)272 void PGOProfilerManager::Reset(const std::shared_ptr<PGOProfiler>& profiler, bool isEnable)
273 {
274 if (isEnable) {
275 isEnable = InitializeData();
276 }
277 if (profiler) {
278 profiler->Reset(isEnable);
279 }
280 }
281
SamplePandaFileInfo(uint32_t checksum,const CString & abcName)282 void PGOProfilerManager::SamplePandaFileInfo(uint32_t checksum, const CString& abcName)
283 {
284 if (pgoInfo_) {
285 pgoInfo_->SamplePandaFileInfoSafe(checksum, abcName);
286 }
287 }
288
SetModuleName(const std::string & moduleName)289 void PGOProfilerManager::SetModuleName(const std::string& moduleName)
290 {
291 PostResetOutPathTask(moduleName);
292 }
293
GetPandaFileId(const CString & abcName,ApEntityId & entryId) const294 bool PGOProfilerManager::GetPandaFileId(const CString& abcName, ApEntityId& entryId) const
295 {
296 if (pgoInfo_) {
297 return pgoInfo_->GetPandaFileIdSafe(abcName, entryId);
298 }
299 return false;
300 }
301
GetPandaFileDesc(ApEntityId abcId,CString & desc) const302 bool PGOProfilerManager::GetPandaFileDesc(ApEntityId abcId, CString& desc) const
303 {
304 if (pgoInfo_) {
305 return pgoInfo_->GetPandaFileDescSafe(abcId, desc);
306 }
307 return false;
308 }
309
SavePGOInfo()310 void PGOProfilerManager::SavePGOInfo()
311 {
312 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PGOProfilerManager::Save");
313 PGOProfilerEncoder encoder(outPath_, apGenMode_);
314 LockHolder lock(GetPGOInfoMutex());
315 encoder.Save(pgoInfo_);
316 }
317
IsDisableAot() const318 bool PGOProfilerManager::IsDisableAot() const
319 {
320 return disableAot_;
321 }
322
SetDisableAot(bool state)323 void PGOProfilerManager::SetDisableAot(bool state)
324 {
325 disableAot_ = state;
326 }
327
SetDisablePGO(bool state)328 void PGOProfilerManager::SetDisablePGO(bool state)
329 {
330 disablePGO_ = state;
331 }
332
DispatchDumpTask()333 void PGOProfilerManager::DispatchDumpTask()
334 {
335 Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<PGODumpTask>(GLOBAL_TASK_ID));
336 }
337
IsProfilerDestroyed(PGOProfiler * profiler)338 bool PGOProfilerManager::IsProfilerDestroyed(PGOProfiler* profiler)
339 {
340 LockHolder lock(profilersMutex_);
341 for (const auto& ptr: profilers_) {
342 if (ptr.get() == profiler) {
343 return false;
344 }
345 }
346 return true;
347 }
348
DumpPendingProfilers()349 void PGOProfilerManager::DumpPendingProfilers()
350 {
351 {
352 ConcurrentGuard guard(v_, "DumpPendingProfilers");
353 std::set<PGOProfiler*> notDumpedProfilers;
354 while (!pendingProfilers_.Empty()) {
355 auto profiler = pendingProfilers_.PopFront();
356 if (profiler == nullptr || IsProfilerDestroyed(profiler)) {
357 continue;
358 }
359 if (profiler->SetStartIfStop()) {
360 profiler->HandlePGODumpByDumpThread();
361 profiler->TrySaveByDumpThread();
362 profiler->SetStopAndNotify();
363 } else if (!IsProfilerDestroyed(profiler)) {
364 notDumpedProfilers.emplace(profiler);
365 }
366 }
367 for (const auto profiler: notDumpedProfilers) {
368 pendingProfilers_.PushBack(profiler);
369 }
370 if (IsForceDump()) {
371 SavePGOInfo();
372 SetForceDump(false);
373 }
374 }
375 LockHolder lock(dumpTaskMutex_);
376 SetIsTaskRunning(false);
377 }
378
TryDispatchDumpTask(PGOProfiler * profiler)379 void PGOProfilerManager::TryDispatchDumpTask(PGOProfiler* profiler)
380 {
381 if (IsForceDump()) {
382 PushAllProfilersToPendingList();
383 } else {
384 pendingProfilers_.PushBack(profiler);
385 }
386 // only one pgo dump task should run at a time
387 LockHolder lock(dumpTaskMutex_);
388 if (IsTaskRunning()) {
389 return;
390 }
391 SetIsTaskRunning(true);
392 DispatchDumpTask();
393 }
394
PushAllProfilersToPendingList()395 void PGOProfilerManager::PushAllProfilersToPendingList()
396 {
397 LockHolder lock(profilersMutex_);
398 for (const auto& prof: profilers_) {
399 pendingProfilers_.PushBack(prof.get());
400 }
401 }
402
IsTaskRunning() const403 bool PGOProfilerManager::IsTaskRunning() const
404 {
405 return isTaskRunning_;
406 }
407
SetIsTaskRunning(bool isTaskRunning)408 void PGOProfilerManager::SetIsTaskRunning(bool isTaskRunning)
409 {
410 isTaskRunning_ = isTaskRunning;
411 }
412
SetForceDump(bool forceDump)413 void PGOProfilerManager::SetForceDump(bool forceDump)
414 {
415 forceDump_ = forceDump;
416 }
417
IsForceDump() const418 bool PGOProfilerManager::IsForceDump() const
419 {
420 return forceDump_;
421 }
422
BinaryToText(const std::string & inPath,const std::string & outPath,uint32_t hotnessThreshold)423 bool PGOProfilerManager::BinaryToText(const std::string& inPath,
424 const std::string& outPath,
425 uint32_t hotnessThreshold)
426 {
427 PGOProfilerDecoder decoder(inPath, hotnessThreshold);
428 if (!decoder.LoadFull()) {
429 return false;
430 }
431 bool ret = decoder.SaveAPTextFile(outPath);
432 decoder.Clear();
433 return ret;
434 }
435
SetIsApFileCompatible(bool isCompatible)436 void PGOProfilerManager::SetIsApFileCompatible(bool isCompatible)
437 {
438 isApFileCompatible_ = isCompatible;
439 }
440
GetIsApFileCompatible() const441 bool PGOProfilerManager::GetIsApFileCompatible() const
442 {
443 return isApFileCompatible_;
444 }
445
GetMaxAotMethodSize() const446 size_t PGOProfilerManager::GetMaxAotMethodSize() const
447 {
448 return maxAotMethodSize_;
449 }
450
SetMaxAotMethodSize(uint32_t value)451 void PGOProfilerManager::SetMaxAotMethodSize(uint32_t value)
452 {
453 maxAotMethodSize_ = value;
454 }
455
IsBigMethod(uint32_t methodSize) const456 bool PGOProfilerManager::IsBigMethod(uint32_t methodSize) const
457 {
458 return maxAotMethodSize_ != 0 && methodSize > maxAotMethodSize_;
459 }
460
GetPGOInfo() const461 std::shared_ptr<PGOInfo> PGOProfilerManager::GetPGOInfo() const
462 {
463 return pgoInfo_;
464 }
465
ResetOutPathByModuleName(const std::string & moduleName)466 bool PGOProfilerManager::ResetOutPathByModuleName(const std::string& moduleName)
467 {
468 LockHolder lock(resetOutPathMutex_);
469 // only first assign takes effect
470 if (!moduleName_.empty() || moduleName.empty()) {
471 return false;
472 }
473 moduleName_ = moduleName;
474 return ResetOutPath(ApNameUtils::GetRuntimeApName(moduleName_));
475 }
476
ResetOutPath(const std::string & fileName)477 bool PGOProfilerManager::ResetOutPath(const std::string& fileName)
478 {
479 return ResetOutPath(outDir_, outPath_, fileName);
480 }
481
ResetOutPath(const std::string & path,std::string & realPath,const std::string & fileName)482 bool PGOProfilerManager::ResetOutPath(const std::string& path, std::string& realPath, const std::string& fileName)
483 {
484 if (!RealPath(path, realPath, false)) {
485 LOG_PGO(ERROR) << "get real path failed, outDir: " << path;
486 return false;
487 }
488
489 auto suffixLength = ApNameUtils::AP_SUFFIX.length();
490 if (realPath.compare(realPath.length() - suffixLength, suffixLength, ApNameUtils::AP_SUFFIX)) {
491 realPath += "/" + fileName;
492 }
493
494 SetSecurityLabel(realPath);
495
496 LOG_PGO(INFO) << "will save profiler to file " << realPath;
497 return true;
498 }
499
RequestAot()500 void PGOProfilerManager::RequestAot()
501 {
502 if (bundleName_.empty() || moduleName_.empty()) {
503 return;
504 }
505
506 LOG_ECMA(INFO) << "Request local aot, bundle: " << bundleName_ << ", module: " << moduleName_;
507 if (!RequestAot(bundleName_, moduleName_, RequestAotMode::RE_COMPILE_ON_IDLE)) {
508 LOG_ECMA(ERROR) << "Request aot failed, bundle: " << bundleName_ << ", module: " << moduleName_;
509 }
510 }
511
PostResetOutPathTask(const std::string & moduleName)512 void PGOProfilerManager::PostResetOutPathTask(const std::string& moduleName)
513 {
514 if (moduleName.empty()) {
515 LOG_PGO(ERROR) << "[" << __func__ << "] module name is empty.";
516 return;
517 }
518 // only post moduleName once
519 bool expected = false;
520 bool desired = true;
521 if (!hasPostModuleName_.compare_exchange_strong(expected, desired)) {
522 return;
523 }
524 Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<ResetOutPathTask>(moduleName, GLOBAL_TASK_ID));
525 }
526
IsInitialized() const527 bool PGOProfilerManager::IsInitialized() const
528 {
529 return isInitialized_;
530 }
531
SetApGenMode(ApGenMode mode)532 void PGOProfilerManager::SetApGenMode(ApGenMode mode)
533 {
534 apGenMode_ = mode;
535 }
536
GetApGenMode() const537 ApGenMode PGOProfilerManager::GetApGenMode() const
538 {
539 return apGenMode_;
540 }
541
GetPGOInfoMutex()542 Mutex& PGOProfilerManager::GetPGOInfoMutex()
543 {
544 static Mutex pgoInfoMutex;
545 return pgoInfoMutex;
546 }
547 } // namespace panda::ecmascript::pgo
548