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#ifndef ECMASCRIPT_PGO_PROFILER_MANAGER_H
17#define ECMASCRIPT_PGO_PROFILER_MANAGER_H
18
19#include <atomic>
20#include <csignal>
21#include <memory>
22
23#include "ecmascript/pgo_profiler/pgo_profiler.h"
24#include "ecmascript/pgo_profiler/pgo_profiler_decoder.h"
25#include "ecmascript/pgo_profiler/pgo_profiler_encoder.h"
26#include "os/mutex.h"
27
28namespace panda::ecmascript::pgo {
29class PGOProfilerManager {
30public:
31    using ApGenMode = PGOProfilerEncoder::ApGenMode;
32    static PGOProfilerManager *PUBLIC_API GetInstance();
33
34    static void SavingSignalHandler(int signo);
35
36    PGOProfilerManager() = default;
37    ~PGOProfilerManager() = default;
38
39    NO_COPY_SEMANTIC(PGOProfilerManager);
40    NO_MOVE_SEMANTIC(PGOProfilerManager);
41
42    void Initialize(const std::string &outDir, uint32_t hotnessThreshold)
43    {
44        // For FA jsvm, merge with existed output file
45        encoder_ = std::make_unique<PGOProfilerEncoder>(outDir, hotnessThreshold, ApGenMode::MERGE);
46    }
47
48    void SetBundleName(const std::string &bundleName)
49    {
50        if (encoder_) {
51            encoder_->SetBundleName(bundleName);
52        }
53    }
54
55    const std::string GetBundleName()
56    {
57        if (encoder_) {
58            return encoder_->GetBundleName();
59        }
60        return "";
61    }
62
63    void SetRequestAotCallback(const RequestAotCallback &cb)
64    {
65        os::memory::LockHolder lock(*mutex_);
66        if (requestAotCallback_ != nullptr) {
67            return;
68        }
69        requestAotCallback_ = cb;
70    }
71
72    bool RequestAot(const std::string &bundleName, const std::string &moduleName, RequestAotMode triggerMode)
73    {
74        RequestAotCallback cb;
75        {
76            os::memory::LockHolder lock(*mutex_);
77            if (requestAotCallback_ == nullptr) {
78                LOG_ECMA(ERROR) << "Trigger aot failed. callback is null.";
79                return false;
80            }
81            cb = requestAotCallback_;
82        }
83        return (cb(bundleName, moduleName, static_cast<int32_t>(triggerMode)) == 0);
84    }
85
86    void Destroy()
87    {
88        if (encoder_) {
89            encoder_->Save();
90            encoder_->Destroy();
91            encoder_.reset();
92        }
93    }
94
95    // Factory
96    std::shared_ptr<PGOProfiler> Build(EcmaVM *vm, bool isEnable)
97    {
98        if (isEnable) {
99            isEnable = InitializeData();
100        }
101        auto profiler = std::make_shared<PGOProfiler>(vm, isEnable);
102        {
103            os::memory::LockHolder lock(*mutex_);
104            profilers_.insert(profiler);
105        }
106        return profiler;
107    }
108
109    // Return false if force disabled or never initialized
110    bool IsEnable() const
111    {
112        return !disablePGO_ && encoder_ && encoder_->IsInitialized();
113    }
114
115    void Destroy(std::shared_ptr<PGOProfiler> &profiler)
116    {
117        if (profiler != nullptr) {
118            profiler->WaitPGODumpFinish();
119            profiler->HandlePGOPreDump();
120            Merge(profiler.get());
121            {
122                os::memory::LockHolder lock(*mutex_);
123                profilers_.erase(profiler);
124            }
125            profiler.reset();
126        }
127    }
128
129    void Reset(const std::shared_ptr<PGOProfiler>& profiler, bool isEnable)
130    {
131        if (isEnable) {
132            isEnable = InitializeData();
133        }
134        if (profiler) {
135            profiler->Reset(isEnable);
136        }
137    }
138
139    void SamplePandaFileInfo(uint32_t checksum, const CString &abcName)
140    {
141        if (encoder_) {
142            encoder_->SamplePandaFileInfo(checksum, abcName);
143        }
144    }
145
146    void SetModuleName(const std::string &moduleName)
147    {
148        if (encoder_) {
149            encoder_->PostResetOutPathTask(moduleName);
150        }
151    }
152
153    bool GetPandaFileId(const CString &abcName, ApEntityId &entryId) const
154    {
155        if (encoder_) {
156            return encoder_->GetPandaFileId(abcName, entryId);
157        }
158        return false;
159    }
160
161    bool GetPandaFileDesc(ApEntityId abcId, CString &desc) const
162    {
163        if (encoder_) {
164            return encoder_->GetPandaFileDesc(abcId, desc);
165        }
166        return false;
167    }
168
169    void SetApGenMode(ApGenMode mode)
170    {
171        if (encoder_) {
172            encoder_->SetApGenMode(mode);
173        }
174    }
175
176    void Merge(PGOProfiler *profiler)
177    {
178        if (encoder_ && profiler->isEnable_) {
179            encoder_->TerminateSaveTask();
180            encoder_->Merge(*profiler->recordInfos_);
181        }
182    }
183
184    void RegisterSavingSignal();
185
186    void AsyncSave()
187    {
188        if (encoder_) {
189            encoder_->PostSaveTask();
190        }
191    }
192
193    bool IsDisableAot() const
194    {
195        return disableAot_;
196    }
197
198    void SetDisableAot(bool state)
199    {
200        disableAot_ = state;
201    }
202
203    // Only set flag to ensure future actions will not trigger PGO path
204    // Caller should handle existing threads and PGO data properly
205    void SetDisablePGO(bool state)
206    {
207        disablePGO_ = state;
208    }
209
210    void ForceSave()
211    {
212        os::memory::LockHolder lock(*mutex_);
213        for (const auto &profiler : profilers_) {
214            profiler->DumpByForce();
215        }
216        GetInstance()->AsyncSave();
217    }
218
219    bool PUBLIC_API TextToBinary(const std::string &inPath, const std::string &outPath, uint32_t hotnessThreshold,
220                                 ApGenMode mode)
221    {
222        PGOProfilerEncoder encoder(outPath, hotnessThreshold, mode);
223        PGOProfilerEncoder decoder(outPath, hotnessThreshold, mode);
224        if (!encoder.InitializeData()) {
225            LOG_ECMA(ERROR) << "PGO Profiler encoder initialized failed";
226            return false;
227        }
228        if (!decoder.InitializeData()) {
229            LOG_ECMA(ERROR) << "PGO Profiler decoder initialized failed";
230            return false;
231        }
232        bool ret = decoder.LoadAPTextFile(inPath);
233        if (ret) {
234            encoder.Merge(decoder);
235            ret = encoder.Save();
236        }
237        encoder.Destroy();
238        decoder.Destroy();
239        return ret;
240    }
241
242    bool PUBLIC_API BinaryToText(const std::string &inPath, const std::string &outPath, uint32_t hotnessThreshold)
243    {
244        PGOProfilerDecoder decoder(inPath, hotnessThreshold);
245        if (!decoder.LoadFull()) {
246            return false;
247        }
248        bool ret = decoder.SaveAPTextFile(outPath);
249        decoder.Clear();
250        return ret;
251    }
252
253    static bool PUBLIC_API MergeApFiles(const std::string &inFiles, const std::string &outPath,
254                                        uint32_t hotnessThreshold, ApGenMode mode);
255    static bool PUBLIC_API MergeApFiles(uint32_t checksum, PGOProfilerDecoder &merger);
256
257    void SetIsApFileCompatible(bool isCompatible)
258    {
259        isApFileCompatible_ = isCompatible;
260    }
261
262    bool GetIsApFileCompatible() const
263    {
264        return isApFileCompatible_;
265    }
266
267    size_t GetMaxAotMethodSize() const
268    {
269        return maxAotMethodSize_;
270    }
271
272    void SetMaxAotMethodSize(uint32_t value)
273    {
274        maxAotMethodSize_ = value;
275    }
276
277    bool IsBigMethod(uint32_t methodSize) const
278    {
279        return maxAotMethodSize_ != 0 && methodSize > maxAotMethodSize_;
280    }
281
282    bool IsEnableForceIC() const
283    {
284        return isEnableForceIC_;
285    }
286
287    void SetEnableForceIC(bool isEnableForceIC)
288    {
289        isEnableForceIC_ = isEnableForceIC;
290    }
291
292private:
293    bool InitializeData()
294    {
295        if (!encoder_) {
296            return false;
297        }
298        bool initializedResult = encoder_->InitializeData();
299        if (initializedResult && !enableSignalSaving_) {
300            RegisterSavingSignal();
301        }
302        return initializedResult;
303    }
304
305    bool disableAot_ {false};
306    bool disablePGO_ {false};
307    std::unique_ptr<PGOProfilerEncoder> encoder_;
308    RequestAotCallback requestAotCallback_;
309    std::atomic_bool enableSignalSaving_ { false };
310    os::memory::Mutex *mutex_ = new os::memory::Mutex();
311    std::set<std::shared_ptr<PGOProfiler>> profilers_;
312    bool isApFileCompatible_ {true};
313    bool isEnableForceIC_ {true};
314    uint32_t maxAotMethodSize_ {0};
315};
316} // namespace panda::ecmascript::pgo
317#endif  // ECMASCRIPT_PGO_PROFILER_MANAGER_H
318