• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_INFO_H
17 #define ECMASCRIPT_PGO_PROFILER_INFO_H
18 
19 #include <memory>
20 #include <sstream>
21 #include <string.h>
22 
23 #include "ecmascript/base/file_header.h"
24 #include "ecmascript/jspandafile/method_literal.h"
25 #include "ecmascript/mem/c_containers.h"
26 #include "ecmascript/mem/native_area_allocator.h"
27 
28 namespace panda::ecmascript {
29 class SaveTask;
30 
31 enum class SampleMode : uint8_t {
32     HOTNESS_MODE,
33     CALL_MODE,
34 };
35 
36 struct SectionInfo {
37     uint32_t offset_ {0};
38     // reserve
39     uint32_t size_ {0};
40     uint32_t number_ {0};
41 };
42 static constexpr size_t ALIGN_SIZE = 4;
43 
44 /**
45  * |----PGOProfilerHeader
46  * |--------MAGIC
47  * |--------VERSION
48  * |--------SECTION_NUMBER
49  * |--------PANDA_FILE_INFO_SECTION_INFO
50  * |------------offset
51  * |------------size (reserve)
52  * |------------number
53  * |--------RECORD_INFO_SECTION_INFO
54  * |------------offset
55  * |------------size (reserve)
56  * |------------number
57  * |----PGOPandaFileInfos
58  * |--------SIZE
59  * |--------CHECK_SUM
60  * |--------...
61  * |----PGORecordDetailInfos
62  * |--------PGOMethodInfoMap
63  * |------------PGOMethodInfo
64  * |----------------size
65  * |----------------id
66  * |----------------count
67  * |----------------mode
68  * |----------------methodName
69  * |----------------...
70  * |----PGORecordSimpleInfos
71  * |--------PGOMethodIdSet
72  * |------------id
73  * |------------...
74  */
75 class PGOProfilerHeader : public base::FileHeader {
76 public:
77     static constexpr VersionType LAST_VERSION = {0, 0, 0, 1};
78     static constexpr size_t SECTION_SIZE = 2;
79     static constexpr size_t PANDA_FILE_SECTION_INDEX = 0;
80     static constexpr size_t RECORD_INFO_SECTION_INDEX = 1;
81 
PGOProfilerHeader()82     PGOProfilerHeader() : base::FileHeader(LAST_VERSION), sectionNumber_(SECTION_SIZE)
83     {
84         GetPandaInfoSection()->offset_ = Size();
85     }
86 
LastSize()87     static size_t LastSize()
88     {
89         return sizeof(PGOProfilerHeader) + (SECTION_SIZE - 1) * sizeof(SectionInfo);
90     }
91 
Size()92     size_t Size() const
93     {
94         return sizeof(PGOProfilerHeader) + (sectionNumber_ - 1) * sizeof(SectionInfo);
95     }
96 
Verify()97     bool Verify() const
98     {
99         return VerifyInner("apPath file", LAST_VERSION);
100     }
101 
Build(PGOProfilerHeader ** header,size_t size)102     static void Build(PGOProfilerHeader **header, size_t size)
103     {
104         *header = reinterpret_cast<PGOProfilerHeader *>(malloc(size));
105         new (*header) PGOProfilerHeader();
106     }
107 
Destroy(PGOProfilerHeader ** header)108     static void Destroy(PGOProfilerHeader **header)
109     {
110         if (*header != nullptr) {
111             free(*header);
112             *header = nullptr;
113         }
114     }
115 
116     // Copy Header.
117     static bool ParseFromBinary(void *buffer, PGOProfilerHeader **header);
118     void ProcessToBinary(std::ofstream &fileStream) const;
119 
120     bool ParseFromText(std::ifstream &stream);
121     bool ProcessToText(std::ofstream &stream) const;
122 
GetPandaInfoSection()123     SectionInfo *GetPandaInfoSection() const
124     {
125         return GetSectionInfo(PANDA_FILE_SECTION_INDEX);
126     }
127 
GetRecordInfoSection()128     SectionInfo *GetRecordInfoSection() const
129     {
130         return GetSectionInfo(RECORD_INFO_SECTION_INDEX);
131     }
132 
133     NO_COPY_SEMANTIC(PGOProfilerHeader);
134     NO_MOVE_SEMANTIC(PGOProfilerHeader);
135 
136 private:
GetSectionInfo(size_t index)137     SectionInfo *GetSectionInfo(size_t index) const
138     {
139         if (index >= SECTION_SIZE) {
140             return nullptr;
141         }
142         return const_cast<SectionInfo *>(&sectionInfos_) + index;
143     }
144 
145     uint32_t sectionNumber_ {SECTION_SIZE};
146     SectionInfo sectionInfos_;
147 };
148 
149 class PGOPandaFileInfos {
150 public:
Sample(uint32_t checksum)151     void Sample(uint32_t checksum)
152     {
153         pandaFileInfos_.insert(checksum);
154     }
155 
Clear()156     void Clear()
157     {
158         pandaFileInfos_.clear();
159     }
160 
161     void ParseFromBinary(void *buffer, SectionInfo *const info);
162     void ProcessToBinary(std::ofstream &fileStream, SectionInfo *info) const;
163 
164     void ProcessToText(std::ofstream &stream) const;
165     bool ParseFromText(std::ifstream &stream);
166 
167     bool CheckSum(uint32_t checksum) const;
168 
169 private:
170     class PandaFileInfo {
171     public:
172         PandaFileInfo() = default;
PandaFileInfo(uint32_t checksum)173         PandaFileInfo(uint32_t checksum) : size_(LastSize()), checksum_(checksum) {}
174 
LastSize()175         static size_t LastSize()
176         {
177             return sizeof(PandaFileInfo);
178         }
179 
Size()180         size_t Size()
181         {
182             return static_cast<size_t>(size_);
183         }
184 
185         bool operator<(const PandaFileInfo &right) const
186         {
187             return checksum_ < right.checksum_;
188         }
189 
GetChecksum()190         uint32_t GetChecksum() const
191         {
192             return checksum_;
193         }
194     private:
195         // Support extended fields
196         uint32_t size_;
197         uint32_t checksum_;
198     };
199 
200     std::set<PandaFileInfo> pandaFileInfos_;
201 };
202 
203 class PGOMethodInfo {
204 public:
205     static constexpr int METHOD_INFO_COUNT = 4;
206     static constexpr int METHOD_ID_INDEX = 0;
207     static constexpr int METHOD_COUNT_INDEX = 1;
208     static constexpr int METHOD_MODE_INDEX = 2;
209     static constexpr int METHOD_NAME_INDEX = 3;
210 
PGOMethodInfo(EntityId id)211     PGOMethodInfo(EntityId id) : id_(id) {}
212 
PGOMethodInfo(EntityId id,uint32_t count,SampleMode mode,const char * methodName)213     PGOMethodInfo(EntityId id, uint32_t count, SampleMode mode, const char *methodName)
214         : id_(id), count_(count), mode_(mode)
215     {
216         size_t len = strlen(methodName);
217         size_ = static_cast<uint32_t>(Size(len));
218         if (len > 0 && memcpy_s(&methodName_, len, methodName, len) != EOK) {
219             LOG_ECMA(ERROR) << "SetMethodName memcpy_s failed" << methodName << ", len = " << len;
220             UNREACHABLE();
221         }
222         *(&methodName_ + len) = '\0';
223     }
224 
Size(uint32_t length)225     static int32_t Size(uint32_t length)
226     {
227         return sizeof(PGOMethodInfo) + AlignUp(length, ALIGN_SIZE);
228     }
229 
Size()230     int32_t Size() const
231     {
232         return size_;
233     }
234 
GetSampleMode(std::string content,SampleMode & mode)235     static bool GetSampleMode(std::string content, SampleMode &mode)
236     {
237         if (content == "HOTNESS_MODE") {
238             mode = SampleMode::HOTNESS_MODE;
239         } else if (content == "CALL_MODE") {
240             mode = SampleMode::CALL_MODE;
241         } else {
242             return false;
243         }
244         return true;
245     }
246 
IncreaseCount()247     void IncreaseCount()
248     {
249         count_++;
250     }
251 
ClearCount()252     void ClearCount()
253     {
254         count_ = 0;
255     }
256 
Merge(const PGOMethodInfo * info)257     void Merge(const PGOMethodInfo *info)
258     {
259         if (!(id_ == info->GetMethodId())) {
260             LOG_ECMA(ERROR) << "The method id must same for merging";
261             return;
262         }
263         count_ += info->GetCount();
264         SetSampleMode(info->GetSampleMode());
265     }
266 
GetMethodId()267     EntityId GetMethodId() const
268     {
269         return id_;
270     }
271 
GetCount()272     uint32_t GetCount() const
273     {
274         return count_;
275     }
276 
GetMethodName()277     const char *GetMethodName() const
278     {
279         return &methodName_;
280     }
281 
SetSampleMode(SampleMode mode)282     void SetSampleMode(SampleMode mode)
283     {
284         if (mode_ == SampleMode::HOTNESS_MODE) {
285             return;
286         }
287         mode_ = mode;
288     }
289 
GetSampleMode()290     SampleMode GetSampleMode() const
291     {
292         return mode_;
293     }
294 
GetSampleModeToString()295     std::string GetSampleModeToString() const
296     {
297         std::string result;
298         switch (mode_) {
299             case SampleMode::HOTNESS_MODE:
300                 result = "HOTNESS_MODE";
301                 break;
302             case SampleMode::CALL_MODE:
303                 result = "CALL_MODE";
304                 break;
305             default:
306                 LOG_ECMA(ERROR) << "mode error";
307         }
308         return result;
309     }
310 
IsFilter(uint32_t threshold)311     bool IsFilter(uint32_t threshold) const
312     {
313         if (count_ < threshold && mode_ == SampleMode::CALL_MODE) {
314             return true;
315         }
316         return false;
317     }
318 
319     void ParseFromBinary(void **buffer);
320     void ProcessToBinary(std::ofstream &fileStream) const;
321 
322     static std::vector<std::string> ParseFromText(const std::string &infoString);
323     void ProcessToText(std::string &text) const;
324 
325     NO_COPY_SEMANTIC(PGOMethodInfo);
326     NO_MOVE_SEMANTIC(PGOMethodInfo);
327 
328 private:
329     uint32_t size_ {0};
330     EntityId id_;
331     uint32_t count_ {0};
332     SampleMode mode_ {SampleMode::CALL_MODE};
333     char methodName_;
334 };
335 
336 class PGOMethodInfoMap {
337 public:
338     PGOMethodInfoMap() = default;
339 
Clear()340     void Clear()
341     {
342         // PGOMethodInfo release by chunk
343         methodInfos_.clear();
344     }
345 
346     bool AddMethod(Chunk *chunk, EntityId methodId, const CString &methodName, SampleMode mode);
347     void Merge(Chunk *chunk, PGOMethodInfoMap *methodInfos);
348 
349     bool ParseFromBinary(uint32_t threshold, void **buffer);
350     bool ProcessToBinary(uint32_t threshold, const CString &recordName, const SaveTask *task,
351         std::ofstream &fileStream) const;
352 
353     bool ParseFromText(Chunk *chunk, uint32_t threshold, const std::vector<std::string> &content);
354     void ProcessToText(uint32_t threshold, const CString &recordName, std::ofstream &stream) const;
355 
356     NO_COPY_SEMANTIC(PGOMethodInfoMap);
357     NO_MOVE_SEMANTIC(PGOMethodInfoMap);
358 
359 private:
360     CMap<EntityId, PGOMethodInfo *> methodInfos_;
361 };
362 
363 class PGOMethodIdSet {
364 public:
365     PGOMethodIdSet() = default;
366 
Match(EntityId methodId)367     bool Match(EntityId methodId)
368     {
369         return methodIdSet_.find(methodId) != methodIdSet_.end();
370     }
371 
372     template <typename Callback>
Update(const CString & recordName,Callback callback)373     bool Update(const CString &recordName, Callback callback)
374     {
375         std::unordered_set<EntityId> newIds = callback(recordName, methodIdSet_);
376         if (!newIds.empty()) {
377             methodIdSet_.insert(newIds.begin(), newIds.end());
378             return true;
379         }
380         return false;
381     }
382 
383     bool ParseFromBinary(uint32_t threshold, void **buffer);
384 
385     NO_COPY_SEMANTIC(PGOMethodIdSet);
386     NO_MOVE_SEMANTIC(PGOMethodIdSet);
387 private:
388     std::unordered_set<EntityId> methodIdSet_;
389 };
390 
391 class PGORecordDetailInfos {
392 public:
PGORecordDetailInfos(uint32_t hotnessThreshold)393     explicit PGORecordDetailInfos(uint32_t hotnessThreshold) : hotnessThreshold_(hotnessThreshold)
394     {
395         chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_);
396     };
397 
~PGORecordDetailInfos()398     ~PGORecordDetailInfos()
399     {
400         Clear();
401     }
402 
Clear()403     void Clear()
404     {
405         for (auto iter : recordInfos_) {
406             iter.second->Clear();
407             nativeAreaAllocator_.Delete(iter.second);
408         }
409         recordInfos_.clear();
410         chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_);
411     }
412 
413     // If it is a new method, return true.
414     bool AddMethod(const CString &recordName, EntityId methodId, const CString &methodName, SampleMode mode);
415     void Merge(const PGORecordDetailInfos &recordInfos);
416 
417     void ParseFromBinary(void *buffer, SectionInfo *const info);
418     void ProcessToBinary(const SaveTask *task, std::ofstream &fileStream, SectionInfo *info) const;
419 
420     bool ParseFromText(std::ifstream &stream);
421     void ProcessToText(std::ofstream &stream) const;
422 
423     NO_COPY_SEMANTIC(PGORecordDetailInfos);
424     NO_MOVE_SEMANTIC(PGORecordDetailInfos);
425 private:
426     uint32_t hotnessThreshold_ {2};
427     NativeAreaAllocator nativeAreaAllocator_;
428     std::unique_ptr<Chunk> chunk_;
429     CMap<CString, PGOMethodInfoMap *> recordInfos_;
430 };
431 
432 class PGORecordSimpleInfos {
433 public:
PGORecordSimpleInfos(uint32_t threshold)434     explicit PGORecordSimpleInfos(uint32_t threshold) : hotnessThreshold_(threshold) {}
~PGORecordSimpleInfos()435     ~PGORecordSimpleInfos()
436     {
437         Clear();
438     }
439 
Clear()440     void Clear()
441     {
442         for (auto iter : methodIds_) {
443             nativeAreaAllocator_.Delete(iter.second);
444         }
445         methodIds_.clear();
446     }
447 
448     bool Match(const CString &recordName, EntityId methodId);
449     template <typename Callback>
Update(Callback callback)450     void Update(Callback callback)
451     {
452         for (auto iter = methodIds_.begin(); iter != methodIds_.end(); iter++) {
453             auto recordName = iter->first;
454             auto methodIds = iter->second;
455             methodIds->Update(recordName, callback);
456         }
457     }
458 
459     template <typename Callback>
Update(const CString & recordName,Callback callback)460     void Update(const CString &recordName, Callback callback)
461     {
462         auto iter = methodIds_.find(recordName);
463         if (iter != methodIds_.end()) {
464             iter->second->Update(recordName, callback);
465         } else {
466             PGOMethodIdSet *methodIds = nativeAreaAllocator_.New<PGOMethodIdSet>();
467             if (methodIds->Update(recordName, callback)) {
468                 methodIds_.emplace(recordName, methodIds);
469             } else {
470                 nativeAreaAllocator_.Delete(methodIds);
471             }
472         }
473     }
474 
475     void ParseFromBinary(void *buffer, SectionInfo *const info);
476 
477     NO_COPY_SEMANTIC(PGORecordSimpleInfos);
478     NO_MOVE_SEMANTIC(PGORecordSimpleInfos);
479 private:
480     uint32_t hotnessThreshold_ {2};
481     NativeAreaAllocator nativeAreaAllocator_;
482     CUnorderedMap<CString, PGOMethodIdSet *> methodIds_;
483 };
484 } // namespace panda::ecmascript
485 #endif  // ECMASCRIPT_PGO_PROFILER_INFO_H
486