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