1 /*
2  * Copyright (c) 2023-2024 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_AP_FILE_POOL_TEMPLATE_H
17 #define ECMASCRIPT_PGO_PROFILER_AP_FILE_POOL_TEMPLATE_H
18 
19 #include <cstdint>
20 #include <fstream>
21 #include <unordered_map>
22 #include <utility>
23 
24 #include "ecmascript/common.h"
25 #include "ecmascript/jspandafile/js_pandafile.h"
26 #include "ecmascript/log_wrapper.h"
27 #include "ecmascript/mem/c_string.h"
28 #include "ecmascript/pgo_profiler/ap_file/pgo_file_info.h"
29 #include "ecmascript/pgo_profiler/pgo_utils.h"
30 #include "macros.h"
31 
32 namespace panda::ecmascript::pgo {
33 template <typename Entry, typename V>
34 class PoolTemplate : public PGOFileSectionInterface {
35 public:
36     using IsReservedCb = std::function<bool(const V &value)>;
37     using GetReservedIdCb = std::function<ApEntityId(const V &value)>;
38 
39     using SupportCb = std::function<bool(PGOProfilerHeader const *header)>;
40     using GetSectionCb = std::function<SectionInfo *(PGOProfilerHeader const *header)>;
41 
PoolTemplate(std::string poolName, uint32_t reservedCount)42     PoolTemplate(std::string poolName, uint32_t reservedCount)
43         : poolName_(std::move(poolName)), RESERVED_COUNT(reservedCount) {};
44 
45     ~PoolTemplate() override
46     {
47         Clear();
48     }
49 
TryAdd(const V &value, ApEntityId &entryId)50     bool TryAdd(const V &value, ApEntityId &entryId)
51     {
52         auto it = valueToId_.find(value);
53         if (it != valueToId_.end()) {
54             entryId = it->second;
55             return true;
56         }
57 
58         entryId = ApEntityId(IsReserved(value) ? (++reservedUsed_, GetReservedId(value))
59                                                : RESERVED_COUNT + pool_.size() - reservedUsed_);
60 
61         auto result = pool_.emplace(entryId, value);
62         valueToId_.emplace(value, entryId);
63         auto &entry = result.first->second;
64         entry.SetEntryId(entryId);
65         return true;
66     }
67 
GetEntryId(const V &value, ApEntityId &entryId) const68     bool GetEntryId(const V &value, ApEntityId &entryId) const
69     {
70         for (const auto &entry : pool_) {
71             if (entry.second.GetData() == value) {
72                 entryId = entry.first;
73                 return true;
74             }
75         }
76         return false;
77     }
78 
GetEntry(ApEntityId id) const79     const Entry *GetEntry(ApEntityId id) const
80     {
81         auto iter = pool_.find(id);
82         if (iter == pool_.end()) {
83             return nullptr;
84         }
85         return &(iter->second);
86     }
87 
GetEntryIdByNormalizedName(const V &value, ApEntityId &entryId) const88     bool GetEntryIdByNormalizedName(const V &value, ApEntityId &entryId) const
89     {
90         for (const auto &entry : pool_) {
91             if (JSPandaFile::GetNormalizedFileDesc(entry.second.GetData()) == value) {
92                 entryId = entry.first;
93                 return true;
94             }
95         }
96         return false;
97     }
98 
Clear()99     void Clear()
100     {
101         pool_.clear();
102         valueToId_.clear();
103         reservedUsed_ = 0;
104     }
105 
Empty() const106     bool Empty() const
107     {
108         return pool_.empty();
109     }
110 
Merge(const PoolTemplate &pool, const std::function<void(ApEntityId, ApEntityId)> &callback)111     void Merge(const PoolTemplate &pool, const std::function<void(ApEntityId, ApEntityId)> &callback)
112     {
113         for (const auto &entry : pool.pool_) {
114             ApEntityId newId(0);
115             TryAdd(entry.second.GetData(), newId);
116             if (callback != nullptr) {
117                 callback(entry.first, newId);
118             }
119         }
120     }
121 
122     uint32_t ProcessToBinary([[maybe_unused]] PGOContext &context, std::fstream &stream) override
123     {
124         LOG_ECMA(DEBUG) << "ProcessToBinary. name: " << poolName_ << ", count: " << pool_.size();
125         SectionInfo secInfo;
126         secInfo.number_ = pool_.size();
127         secInfo.offset_ = sizeof(SectionInfo);
128         auto secInfoPos = stream.tellp();
129         stream.seekp(secInfo.offset_, std::ofstream::cur);
130         for (auto &entry : pool_) {
131             stream.write(reinterpret_cast<const char *>(&(entry.first)), sizeof(ApEntityId));
132             entry.second.ProcessToBinary(context, stream);
133         }
134         secInfo.size_ = static_cast<uint32_t>(stream.tellp()) - static_cast<uint32_t>(secInfoPos);
135         auto tail = stream.tellp();
136         stream.seekp(secInfoPos, std::ofstream::beg);
137         stream.write(reinterpret_cast<const char *>(&(secInfo)), sizeof(SectionInfo));
138         stream.seekp(tail, std::ofstream::beg);
139         return 1;
140     }
141 
142     bool ProcessToText(std::ofstream &stream) override
143     {
144         bool isFirst = true;
145         for (auto &entry : pool_) {
146             if (isFirst) {
147                 stream << DumpUtils::NEW_LINE;
148                 stream << poolName_;
149                 stream << DumpUtils::BLOCK_START;
150                 isFirst = false;
151             }
152             stream << DumpUtils::NEW_LINE;
153             stream << std::to_string(entry.first);
154             stream << DumpUtils::SPACE;
155             stream << DumpUtils::ARRAY_START;
156             entry.second.ProcessToText(stream);
157             stream << DumpUtils::ARRAY_END;
158         }
159         if (!isFirst) {
160             stream << (DumpUtils::SPACE + DumpUtils::NEW_LINE);
161         }
162         return true;
163     }
164 
ProcessToJson(std::vector<ProfileType::StringMap> &abcPoolArray)165     void ProcessToJson(std::vector<ProfileType::StringMap> &abcPoolArray)
166     {
167         for (auto &entry : pool_) {
168             ProfileType::StringMap abcPool;
169             abcPool.insert(std::make_pair(DumpJsonUtils::ABC_ID, std::to_string(entry.first)));
170             abcPool.insert(std::make_pair(DumpJsonUtils::ABC_FILE, entry.second.GetData()));
171             abcPoolArray.push_back(abcPool);
172         }
173     }
174     uint32_t ParseFromBinary([[maybe_unused]] PGOContext &context, void **buffer,
175                              PGOProfilerHeader const *header) override
176     {
177         auto secInfo = base::ReadBuffer<SectionInfo>(buffer);
178         for (uint32_t i = 0; i < secInfo.number_; i++) {
179             auto entryId = base::ReadBuffer<ApEntityId>(buffer, sizeof(ApEntityId));
180             auto result = pool_.try_emplace(entryId);
181             result.first->second.SetEntryId(entryId);
182             result.first->second.ParseFromBinary(context, buffer, header);
183         }
184         return 1;
185     }
186 
SetIsReservedCb(const IsReservedCb &isReservedCb)187     void SetIsReservedCb(const IsReservedCb &isReservedCb)
188     {
189         isReservedCb_ = isReservedCb;
190     }
191 
SetGetReservedIdCb(const GetReservedIdCb &getReservedIdCb)192     void SetGetReservedIdCb(const GetReservedIdCb &getReservedIdCb)
193     {
194         getReservedIdCb_ = getReservedIdCb;
195     }
196 
SetSupportCb(const SupportCb &supportCb)197     void SetSupportCb(const SupportCb &supportCb)
198     {
199         supportCb_ = supportCb;
200     }
201 
SetGetSectionCb(const GetSectionCb &getSectionCb)202     void SetGetSectionCb(const GetSectionCb &getSectionCb)
203     {
204         getSectionCb_ = getSectionCb;
205     }
206 
GetPool()207     std::unordered_map<ApEntityId, Entry> &GetPool()
208     {
209         return pool_;
210     }
211 
GetValueToId()212     std::unordered_map<V, ApEntityId> &GetValueToId()
213     {
214         return valueToId_;
215     }
216 
217 private:
218     NO_COPY_SEMANTIC(PoolTemplate);
219     NO_MOVE_SEMANTIC(PoolTemplate);
220 
221     bool Support(PGOProfilerHeader const *header) const override
222     {
223         return supportCb_(header);
224     }
225 
226     SectionInfo *GetSection(PGOProfilerHeader const *header) const override
227     {
228         return getSectionCb_(header);
229     }
230 
IsReserved(const V &value)231     bool IsReserved(const V &value)
232     {
233         return isReservedCb_(value);
234     }
235 
GetReservedId(const V &value)236     ApEntityId GetReservedId(const V &value)
237     {
238         return getReservedIdCb_(value);
239     }
240 
241     const std::string poolName_;
242     const uint32_t RESERVED_COUNT {};
243     uint32_t reservedUsed_ {0};
244 
245     IsReservedCb isReservedCb_;
246     GetReservedIdCb getReservedIdCb_;
247     SupportCb supportCb_;
248     GetSectionCb getSectionCb_;
249     std::unordered_map<ApEntityId, Entry> pool_;
250     std::unordered_map<V, ApEntityId> valueToId_;
251 };
252 }  // namespace panda::ecmascript::pgo
253 
254 namespace std {
255 using panda::ecmascript::pgo::ProfileType;
256 template<>
257 struct hash<ProfileType> {
258     size_t operator()(const ProfileType& type) const noexcept
259     {
260         return type.GetRaw();
261     }
262 };
263 } // namespace std
264 #endif  // ECMASCRIPT_PGO_PROFILER_AP_FILE_POOL_TEMPLATE_H
265