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#include <fstream>
18
19#include "ecmascript/platform/file.h"
20namespace panda::ecmascript::pgo {
21namespace {
22    constexpr int32_t PGO_SAVING_SIGNAL = 50;
23} // namespace
24
25PGOProfilerManager* PGOProfilerManager::GetInstance()
26{
27    static PGOProfilerManager* instance = new PGOProfilerManager();
28    return instance;
29}
30
31bool PGOProfilerManager::MergeApFiles(const std::string &inFiles, const std::string &outPath, uint32_t hotnessThreshold,
32                                      ApGenMode mode)
33{
34    arg_list_t apFileNames = base::StringHelper::SplitString(inFiles, GetFileDelimiter());
35    PGOProfilerEncoder merger(outPath, hotnessThreshold, mode);
36    if (!merger.InitializeData()) {
37        LOG_ECMA(ERROR) << "PGO Profiler encoder initialized failed. outPath: " << outPath
38                        << " ,hotnessThreshold: " << hotnessThreshold;
39        return false;
40    }
41    bool hasMerged = false;
42    std::string firstApFileName;
43    for (const auto &fileName : apFileNames) {
44        if (!base::StringHelper::EndsWith(fileName, ".ap")) {
45            LOG_ECMA(ERROR) << "The file path (" << fileName << ") does not end with .ap";
46            continue;
47        }
48        PGOProfilerDecoder decoder(fileName, hotnessThreshold);
49        if (!decoder.LoadFull(merger.GetAbcFilePool())) {
50            LOG_ECMA(ERROR) << "Fail to load file path (" << fileName << "), skip it.";
51            continue;
52        }
53        if (!hasMerged) {
54            firstApFileName = fileName;
55        } else {
56            if (!merger.VerifyPandaFileMatched(decoder.GetPandaFileInfos(), firstApFileName, fileName)) {
57                continue;
58            }
59        }
60        if (!decoder.IsCompatibleWithAOTFile()) {
61            LOG_ECMA(ERROR) << "The ap file (" << fileName << ") is not compatible with AOT version. skip it";
62            continue;
63        }
64        merger.Merge(decoder.GetRecordDetailInfos());
65        merger.Merge(decoder.GetPandaFileInfos());
66        hasMerged = true;
67    }
68    if (!hasMerged) {
69        LOG_ECMA(ERROR)
70            << "No ap file pass verify, no ap file compatible an version, no ap file processed. Input files: "
71            << inFiles;
72        GetInstance()->SetIsApFileCompatible(false);
73        return false;
74    }
75    GetInstance()->SetIsApFileCompatible(true);
76    merger.Save();
77    return true;
78}
79
80bool PGOProfilerManager::MergeApFiles(uint32_t checksum, PGOProfilerDecoder &merger)
81{
82    uint32_t hotnessThreshold = merger.GetHotnessThreshold();
83    std::string inFiles(merger.GetInPath());
84    arg_list_t pandaFileNames = base::StringHelper::SplitString(inFiles, GetFileDelimiter());
85    if (pandaFileNames.empty()) {
86        return true;
87    }
88    merger.InitMergeData();
89    bool hasMerged = false;
90    std::string firstApFileName;
91    for (const auto &fileName : pandaFileNames) {
92        PGOProfilerDecoder decoder(fileName, hotnessThreshold);
93        if (!decoder.LoadAndVerify(checksum, merger.GetAbcFilePool())) {
94            LOG_ECMA(ERROR) << "Load and verify file (" << fileName << ") failed, skip it.";
95            continue;
96        }
97        if (!hasMerged) {
98            firstApFileName = fileName;
99        } else {
100            if (!merger.GetPandaFileInfos().VerifyChecksum(decoder.GetPandaFileInfos(), firstApFileName, fileName)) {
101                continue;
102            }
103        }
104        if (!decoder.IsCompatibleWithAOTFile()) {
105            LOG_ECMA(ERROR) << "The ap file (" << fileName << ") is not compatible with AOT version. skip it";
106            continue;
107        }
108        merger.Merge(decoder);
109        hasMerged = true;
110    }
111    if (!hasMerged) {
112        LOG_ECMA(ERROR)
113            << "No ap file pass verify, no ap file compatible an version, no ap file processed. Input files: "
114            << inFiles;
115        GetInstance()->SetIsApFileCompatible(false);
116        return false;
117    }
118    GetInstance()->SetIsApFileCompatible(true);
119    return true;
120}
121
122void PGOProfilerManager::RegisterSavingSignal()
123{
124    LOG_ECMA(INFO) << "Register Pgo Saving Signal";
125    if (encoder_ == nullptr) {
126        LOG_ECMA(ERROR) << "Can not register pgo saving signal, because encoder is null.";
127        return;
128    }
129    if (!encoder_->IsInitialized()) {
130        LOG_ECMA(DEBUG) << "Can not register pgo saving signal, because encoder is initialized.";
131        return;
132    }
133    signal(PGO_SAVING_SIGNAL, SavingSignalHandler);
134    enableSignalSaving_ = true;
135}
136
137void PGOProfilerManager::SavingSignalHandler(int signo)
138{
139    if (signo != PGO_SAVING_SIGNAL) {
140        return;
141    }
142
143    PGOProfilerManager::GetInstance()->ForceSave();
144}
145} // namespace panda::ecmascript::pgo
146