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