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