• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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_DFX_PGO_PROFILER_MANAGER_H
17 #define ECMASCRIPT_DFX_PGO_PROFILER_MANAGER_H
18 
19 #include <algorithm>
20 #include <cstring>
21 #include <memory>
22 
23 #include "ecmascript/base/file_header.h"
24 #include "ecmascript/ecma_macros.h"
25 #include "ecmascript/ecma_vm.h"
26 #include "ecmascript/log_wrapper.h"
27 #include "ecmascript/mem/chunk_containers.h"
28 #include "ecmascript/mem/native_area_allocator.h"
29 #include "ecmascript/taskpool/task.h"
30 
31 namespace panda::ecmascript {
32 enum class SampleMode : uint8_t {
33     HOTNESS_MODE,
34     CALL_MODE,
35 };
36 
37 /*
38  * Support statistics of JS Function call heat. Save the method ID whose calls are less than MIN_COUNT.
39  *
40  * The saving format is as follows:
41  * "recordName1:[methodId/count/mode/name,methodId/count/mode/name......]"
42  * "recordName2:[methodId/count/mode/name,methodId/count/mode/name,methodId/count/mode/name......]"
43  *                                     "......"
44  * */
45 class MethodProfilerInfo {
46 public:
47     static constexpr size_t ALIGN_SIZE = 4;
48 
MethodProfilerInfo(EntityId id,uint32_t count,SampleMode mode,uint16_t length)49     MethodProfilerInfo(EntityId id, uint32_t count, SampleMode mode, uint16_t length)
50         : id_(id), count_(count), mode_(mode), methodLength_(length) {}
51 
Size(uint32_t length)52     static int32_t Size(uint32_t length)
53     {
54         return sizeof(MethodProfilerInfo) + AlignUp(length, ALIGN_SIZE);
55     }
56 
Size()57     int32_t Size()
58     {
59         return sizeof(MethodProfilerInfo) + AlignUp(methodLength_, ALIGN_SIZE);
60     }
61 
IncreaseCount()62     void IncreaseCount()
63     {
64         count_++;
65     }
66 
ClearCount()67     void ClearCount()
68     {
69         count_ = 0;
70     }
71 
Merge(const MethodProfilerInfo * info)72     void Merge(const MethodProfilerInfo *info)
73     {
74         count_ += info->GetCount();
75         methodLength_ = info->GetMethodLength();
76         SetSampleMode(info->GetSampleMode());
77         SetMethodName(info->GetMethodName(), info->GetMethodLength());
78     }
79 
GetMethodId()80     EntityId GetMethodId() const
81     {
82         return id_;
83     }
84 
GetCount()85     uint32_t GetCount() const
86     {
87         return count_;
88     }
89 
GetMethodLength()90     uint16_t GetMethodLength() const
91     {
92         return methodLength_;
93     }
94 
SetMethodName(const char * methodName,size_t len)95     void SetMethodName(const char *methodName, size_t len)
96     {
97         if (memcpy_s(&methodName_, methodLength_, methodName, len) != EOK) {
98             LOG_ECMA(ERROR) << "SetMethodName memcpy_s failed" << methodName << ", len = " << len;
99         }
100         *(&methodName_ + len) = '\0';
101     }
102 
GetMethodName()103     const char *GetMethodName() const
104     {
105         return &methodName_;
106     }
107 
SetSampleMode(SampleMode mode)108     void SetSampleMode(SampleMode mode)
109     {
110         if (mode_ == SampleMode::HOTNESS_MODE) {
111             return;
112         }
113         mode_ = mode;
114     }
115 
GetSampleMode()116     SampleMode GetSampleMode() const
117     {
118         return mode_;
119     }
120 
121     NO_COPY_SEMANTIC(MethodProfilerInfo);
122     NO_MOVE_SEMANTIC(MethodProfilerInfo);
123 
124 private:
125     EntityId id_;
126     uint32_t count_ {0};
127     SampleMode mode_ {SampleMode::CALL_MODE};
128     uint16_t methodLength_ {0};
129     char methodName_ {'\0'};
130 };
131 
132 class PGOProfilerHeader : public base::FileHeader {
133 public:
134     static constexpr std::array<uint8_t, VERSION_SIZE> LAST_VERSION = {0, 0, 0, 1};
135 
PGOProfilerHeader()136     PGOProfilerHeader() : base::FileHeader(LAST_VERSION) {}
137 
Verify()138     bool Verify()
139     {
140         return VerifyInner(LAST_VERSION);
141     }
142 };
143 
144 class PandaFileProfilerInfo {
145 public:
146     PandaFileProfilerInfo() = default;
PandaFileProfilerInfo(uint32_t checksum)147     PandaFileProfilerInfo(uint32_t checksum) : checksum_(checksum) {}
148 
GetChecksum()149     uint32_t GetChecksum()
150     {
151         return checksum_;
152     }
153 
154 private:
155     uint32_t checksum_;
156 };
157 
158 class PGOProfiler {
159 public:
160     NO_COPY_SEMANTIC(PGOProfiler);
161     NO_MOVE_SEMANTIC(PGOProfiler);
162 
163     void Sample(JSTaggedType value, SampleMode mode = SampleMode::CALL_MODE);
164 private:
PGOProfiler(EcmaVM * vm,bool isEnable)165     PGOProfiler(EcmaVM *vm, bool isEnable)
166         : isEnable_(isEnable), chunk_(vm->GetNativeAreaAllocator()), profilerMap_(&chunk_) {};
167     virtual ~PGOProfiler();
Reset(bool isEnable)168     void Reset(bool isEnable)
169     {
170         isEnable_ = isEnable;
171         profilerMap_.clear();
172         methodCount_ = 0;
173     }
174 
175     static constexpr uint32_t MERGED_EVERY_COUNT = 10;
176     bool isEnable_ {false};
177     Chunk chunk_;
178     ChunkUnorderedMap<CString, ChunkUnorderedMap<EntityId, MethodProfilerInfo *> *> profilerMap_;
179     uint32_t methodCount_ {0};
180     friend class PGOProfilerManager;
181 };
182 
183 class PGOProfilerManager {
184 public:
GetInstance()185     static PGOProfilerManager *GetInstance()
186     {
187         static PGOProfilerManager instance;
188         return &instance;
189     }
190 
191     PGOProfilerManager() = default;
192     ~PGOProfilerManager() = default;
193 
194     NO_COPY_SEMANTIC(PGOProfilerManager);
195     NO_MOVE_SEMANTIC(PGOProfilerManager);
196 
197     void Initialize(uint32_t hotnessThreshold, const std::string &outDir);
198     bool InitializeData();
199 
200     void Destroy();
201 
202     // Factory
Build(EcmaVM * vm,bool isEnable)203     PGOProfiler *Build(EcmaVM *vm, bool isEnable)
204     {
205         if (isEnable) {
206             isEnable = InitializeData();
207         }
208         return new PGOProfiler(vm, isEnable);
209     }
210 
Reset(PGOProfiler * profiler,bool isEnable)211     void Reset(PGOProfiler *profiler, bool isEnable)
212     {
213         if (profiler) {
214             profiler->Reset(isEnable);
215         }
216         if (isEnable) {
217             InitializeData();
218         }
219     }
220 
Destroy(PGOProfiler * profiler)221     void Destroy(PGOProfiler *profiler)
222     {
223         if (profiler != nullptr) {
224             Merge(profiler);
225             delete profiler;
226         }
227     }
228 
229     void SamplePandaFileInfo(uint32_t checksum);
230     void Merge(PGOProfiler *profile);
231     void TerminateSaveTask();
232     void PostSaveTask();
233 
234 private:
235     class SaveTask : public Task {
236     public:
SaveTask(int32_t id)237         SaveTask(int32_t id) : Task(id) {};
238         virtual ~SaveTask() = default;
239 
Run(uint32_t threadIndex)240         bool Run([[maybe_unused]] uint32_t threadIndex) override
241         {
242             PGOProfilerManager::GetInstance()->StartSaveTask(this);
243             return true;
244         }
245 
GetTaskType()246         TaskType GetTaskType() override
247         {
248             return TaskType::PGO_SAVE_TASK;
249         }
250 
251         NO_COPY_SEMANTIC(SaveTask);
252         NO_MOVE_SEMANTIC(SaveTask);
253     };
254 
255     void StartSaveTask(SaveTask *task);
256     void SaveProfiler(SaveTask *task = nullptr);
257     void ProcessProfileHeader(std::ofstream &fileStream);
258     void ProcessPandaFileInfo(std::ofstream &fileStream);
259     void ProcessProfile(std::ofstream &fileStream, SaveTask *task);
260 
261     bool isInitialized_ {false};
262     uint32_t hotnessThreshold_ {2};
263     std::string outDir_;
264     std::string realOutPath_;
265     std::unique_ptr<NativeAreaAllocator> nativeAreaAllocator_;
266     std::unique_ptr<Chunk> chunk_;
267     PGOProfilerHeader header_;
268     ChunkUnorderedMap<CString, ChunkUnorderedMap<EntityId, MethodProfilerInfo *> *> *globalProfilerMap_;
269     ChunkVector<PandaFileProfilerInfo *> *pandaFileProfilerInfos_;
270     os::memory::Mutex mutex_;
271 };
272 
273 } // namespace panda::ecmascript
274 #endif  // ECMASCRIPT_DFX_PGO_PROFILER_MANAGER_H
275