1/*
2 * Copyright (c) 2022 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_decoder.h"
17#include <memory>
18
19#include "ecmascript/platform/file.h"
20#include "ecmascript/pgo_profiler/pgo_profiler_info.h"
21
22namespace panda::ecmascript::pgo {
23bool PGOProfilerDecoder::Load(const std::shared_ptr<PGOAbcFilePool> &externalAbcFilePool)
24{
25    if (isLoaded_) {
26        Clear();
27    }
28    if (!LoadAPBinaryFile()) {
29        return false;
30    }
31    void *addr = fileMapAddr_.GetOriginAddr();
32
33    if (!PGOProfilerHeader::ParseFromBinary(addr, fileMapAddr_.GetSize(), &header_)) {
34        UnLoadAPBinaryFile();
35        LOG_ECMA(ERROR) << "Parse profiler header failed";
36        return false;
37    }
38    pandaFileInfos_.ParseFromBinary(addr, header_->GetPandaInfoSection());
39
40    if (!recordSimpleInfos_) {
41        recordSimpleInfos_ = std::make_unique<PGORecordSimpleInfos>(hotnessThreshold_);
42    }
43    LoadAbcIdPool(externalAbcFilePool, *recordSimpleInfos_, addr);
44    recordSimpleInfos_->ParseFromBinary(addr, header_, abcFilePool_);
45    UnLoadAPBinaryFile();
46
47    isLoaded_ = true;
48    return true;
49}
50
51bool PGOProfilerDecoder::Verify(uint32_t checksum)
52{
53    if (!isLoaded_) {
54        return false;
55    }
56    // Notice: lx maybe can support method checksum;
57    return pandaFileInfos_.Checksum(checksum);
58}
59
60bool PGOProfilerDecoder::LoadAndVerify(uint32_t checksum, const std::shared_ptr<PGOAbcFilePool> &externalAbcFilePool)
61{
62    // The file does not exist. Enter full compiler mode.
63    if (inPath_.empty()) {
64        LOG_ECMA(INFO) << "When the file is empty. Enter full compiler mode.";
65        Clear();
66        return true;
67    }
68    if (Load(externalAbcFilePool) && Verify(checksum)) {
69        return true;
70    }
71    return false;
72}
73
74bool PGOProfilerDecoder::LoadFull(const std::shared_ptr<PGOAbcFilePool> &externalAbcFilePool)
75{
76    if (isLoaded_) {
77        Clear();
78    }
79    // profiler dump tools may write data to memory when merge ap files.
80    if (!LoadAPBinaryFile(PAGE_PROT_READWRITE)) {
81        return false;
82    }
83    void *addr = fileMapAddr_.GetOriginAddr();
84
85    if (!PGOProfilerHeader::ParseFromBinary(addr, fileMapAddr_.GetSize(), &header_)) {
86        UnLoadAPBinaryFile();
87        LOG_ECMA(ERROR) << "Parse profiler header failed";
88        return false;
89    }
90    pandaFileInfos_.ParseFromBinary(addr, header_->GetPandaInfoSection());
91    if (!recordDetailInfos_) {
92        recordDetailInfos_ = std::make_shared<PGORecordDetailInfos>(hotnessThreshold_);
93    }
94
95    LoadAbcIdPool(externalAbcFilePool, *recordDetailInfos_, addr);
96    if (!recordDetailInfos_->ParseFromBinary(addr, header_)) {
97        return false;
98    }
99    recordDetailInfos_->ResetAbcIdRemap();
100    isLoaded_ = true;
101    return true;
102}
103
104void PGOProfilerDecoder::LoadAbcIdPool(const std::shared_ptr<PGOAbcFilePool> &externalAbcFilePool,
105                                       PGOContext &context, void *addr)
106{
107    if (externalAbcFilePool != nullptr) {
108        abcFilePool_ = externalAbcFilePool;
109        externalAbcFilePool_ = true;
110    } else {
111        abcFilePool_ = std::make_unique<PGOAbcFilePool>();
112        externalAbcFilePool_ = false;
113    }
114
115    if (header_->SupportProfileTypeWithAbcId()) {
116        auto abcFilePoolTemp = std::make_shared<PGOAbcFilePool>();
117        PGOFileSectionInterface::ParseSectionFromBinary(context, addr, header_, *abcFilePoolTemp->GetPool());
118        // step1: [abc pool merge] merge abcFilePool from ap file to memory.
119        abcFilePool_->Merge(context, *abcFilePoolTemp);
120    }
121}
122
123bool PGOProfilerDecoder::SaveAPTextFile(const std::string &outPath)
124{
125    if (!isLoaded_) {
126        return false;
127    }
128    std::string realOutPath;
129    if (!RealPath(outPath, realOutPath, false)) {
130        return false;
131    }
132    std::ofstream fileStream(realOutPath.c_str());
133    if (!fileStream.is_open()) {
134        LOG_ECMA(ERROR) << "The file path(" << realOutPath << ") open failure!";
135        return false;
136    }
137    if (header_ == nullptr) {
138        LOG_ECMA(FATAL) << "PGOProfilerDecoder::SaveAPTextFile:header_ is nullptr";
139    }
140    if (!header_->ProcessToText(fileStream)) {
141        return false;
142    }
143    pandaFileInfos_.ProcessToText(fileStream);
144    recordDetailInfos_->ProcessToText(fileStream);
145    abcFilePool_->GetPool()->ProcessToText(fileStream);
146    return true;
147}
148
149bool PGOProfilerDecoder::LoadAPBinaryFile(int prot)
150{
151    std::string realPath;
152    if (!RealPath(inPath_, realPath)) {
153        return false;
154    }
155
156    static const std::string endString = ".ap";
157    if (realPath.compare(realPath.length() - endString.length(), endString.length(), endString)) {
158        LOG_ECMA(ERROR) << "The file path( " << realPath << ") does not end with .ap";
159        return false;
160    }
161    LOG_ECMA(INFO) << "Load profiler from file:" << realPath;
162    fileMapAddr_ = FileMap(realPath.c_str(), FILE_RDONLY, prot);
163    if (fileMapAddr_.GetOriginAddr() == nullptr) {
164        LOG_ECMA(ERROR) << "File mmap failed";
165        return false;
166    }
167    return true;
168}
169
170void PGOProfilerDecoder::UnLoadAPBinaryFile()
171{
172    if (fileMapAddr_.GetOriginAddr() != nullptr && fileMapAddr_.GetSize() > 0) {
173        FileUnMap(fileMapAddr_);
174        fileMapAddr_.Reset();
175    }
176}
177
178void PGOProfilerDecoder::Clear()
179{
180    if (isLoaded_) {
181        UnLoadAPBinaryFile();
182        isVerifySuccess_ = true;
183        hotnessThreshold_ = 0;
184        PGOProfilerHeader::Destroy(&header_);
185        pandaFileInfos_.Clear();
186        if (abcFilePool_ && !externalAbcFilePool_) {
187            abcFilePool_->Clear();
188        }
189        if (recordDetailInfos_) {
190            recordDetailInfos_->Clear();
191        }
192        if (recordSimpleInfos_) {
193            recordSimpleInfos_->Clear();
194        }
195        isLoaded_ = false;
196    }
197}
198
199bool PGOProfilerDecoder::Match(const JSPandaFile *jsPandaFile, const CString &recordName, PGOMethodId methodId)
200{
201    if (!isLoaded_) {
202        return true;
203    }
204    if (!isVerifySuccess_) {
205        return false;
206    }
207    return recordSimpleInfos_->Match(GetNormalizedFileDesc(jsPandaFile), recordName, EntityId(methodId));
208}
209
210bool PGOProfilerDecoder::GetHClassTreeDesc(PGOSampleType profileType, PGOHClassTreeDesc **desc) const
211{
212    if (!isLoaded_ || !isVerifySuccess_) {
213        return false;
214    }
215    return recordSimpleInfos_->GetHClassTreeDesc(profileType, desc);
216}
217
218void PGOProfilerDecoder::GetMismatchResult(const JSPandaFile *jsPandaFile, uint32_t &totalMethodCount,
219                                           uint32_t &mismatchMethodCount,
220                                           std::set<std::pair<std::string, CString>> &mismatchMethodSet) const
221{
222    if (!isLoaded_ || !isVerifySuccess_) {
223        return;
224    }
225    return recordSimpleInfos_->GetMismatchResult(GetNormalizedFileDesc(jsPandaFile), totalMethodCount,
226                                                 mismatchMethodCount, mismatchMethodSet);
227}
228
229CString PGOProfilerDecoder::GetNormalizedFileDesc(const JSPandaFile *jsPandaFile) const
230{
231    ASSERT(jsPandaFile != nullptr);
232    if (header_->SupportProfileTypeWithAbcId()) {
233        return jsPandaFile->GetNormalizedFileDesc();
234    }
235    return "";
236}
237
238bool PGOProfilerDecoder::InitMergeData()
239{
240    ASSERT(!isLoaded_);
241    if (!recordSimpleInfos_) {
242        recordSimpleInfos_ = std::make_unique<PGORecordSimpleInfos>(hotnessThreshold_);
243    }
244    if (!header_) {
245        // For merge scene, we only care about the ap capability which is in the version field.
246        PGOProfilerHeader::Build(&header_, sizeof(PGOProfilerHeader));
247        ASSERT(header_ != nullptr);
248        memset_s(header_, sizeof(PGOProfilerHeader), 0, sizeof(PGOProfilerHeader));
249    }
250    if (!abcFilePool_) {
251        abcFilePool_ = std::make_shared<PGOAbcFilePool>();
252        externalAbcFilePool_ = false;
253    }
254    isLoaded_ = true;
255    isVerifySuccess_ = true;
256    return true;
257}
258
259void PGOProfilerDecoder::Merge(const PGOProfilerDecoder &decoder)
260{
261    if (!isLoaded_ || !isVerifySuccess_) {
262        return;
263    }
264    // For merge scene, we chose the highest version from input ap files
265    if (!(header_->CompatibleVerify(decoder.header_->GetVersion()))) {
266        // For merge scene, we only care about the ap capability which is in the version field.
267        memcpy_s(header_, sizeof(base::FileHeaderBase), decoder.header_, sizeof(base::FileHeaderBase));
268    }
269    pandaFileInfos_.Merge(decoder.GetPandaFileInfos());
270    recordSimpleInfos_->Merge(decoder.GetRecordSimpleInfos());
271}
272} // namespace panda::ecmascript::pgo
273