1 /*
2  * Copyright (c) 2021-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_DFX_HPROF_HEAP_PROFILER_H
17 #define ECMASCRIPT_DFX_HPROF_HEAP_PROFILER_H
18 
19 #include "ecmascript/ecma_macros.h"
20 #include "ecmascript/dfx/hprof/file_stream.h"
21 #include "ecmascript/dfx/hprof/heap_profiler_interface.h"
22 #include "ecmascript/dfx/hprof/heap_snapshot_json_serializer.h"
23 #include "ecmascript/dfx/hprof/heap_tracker.h"
24 #include "ecmascript/dfx/hprof/heap_sampling.h"
25 #include "ecmascript/dfx/hprof/progress.h"
26 #include "ecmascript/dfx/hprof/string_hashmap.h"
27 #include "ecmascript/mem/c_containers.h"
28 
29 namespace panda::ecmascript {
30 class HeapSnapshot;
31 class EcmaVM;
32 using NodeId = uint64_t;
33 
34 class EntryIdMap {
35 public:
36     EntryIdMap() = default;
37     ~EntryIdMap() = default;
38     NO_COPY_SEMANTIC(EntryIdMap);
39     NO_MOVE_SEMANTIC(EntryIdMap);
40 
41     static constexpr uint64_t SEQ_STEP = 2;
42     std::pair<bool, NodeId> FindId(JSTaggedType addr);
43     bool InsertId(JSTaggedType addr, NodeId id);
44     bool EraseId(JSTaggedType addr);
45     bool Move(JSTaggedType oldAddr, JSTaggedType forwardAddr);
46     void UpdateEntryIdMap(HeapSnapshot *snapshot);
GetNextId()47     NodeId GetNextId()
48     {
49         nextId_ += SEQ_STEP;
50         return nextId_ - SEQ_STEP;
51     }
GetLastId()52     NodeId GetLastId()
53     {
54         return nextId_ - SEQ_STEP;
55     }
GetIdCount()56     size_t GetIdCount()
57     {
58         return idMap_.size();
59     }
GetIdMap()60     CUnorderedMap<JSTaggedType, NodeId>* GetIdMap()
61     {
62         return &idMap_;
63     }
GetId()64     NodeId GetId()
65     {
66         return nextId_;
67     }
SetId(NodeId id)68     void SetId(NodeId id)
69     {
70         nextId_ = id;
71     }
72 
73 private:
74     NodeId nextId_ {3U};  // 1 Reversed for SyntheticRoot
75     CUnorderedMap<JSTaggedType, NodeId> idMap_ {};
76 };
77 
78 struct AddrTableItem {
79     uint64_t addr;
80     uint64_t id;
81     uint64_t objSize;
82     uint64_t offset; // offset to the file
83     uint64_t stringId;
84 };
85 
86 struct RawHeapObjInfo {
87     AddrTableItem *tInfo;
88     char *newAddr;
89     bool isRoot;
90     CUnorderedSet<uint64_t> refSet;
91 };
92 
93 struct RawHeapInfoArgs {
94     CVector<RawHeapObjInfo *> rawObjInfoVec;
95     CUnorderedMap<uint64_t, RawHeapObjInfo *> oldAddrMapObjInfo;
96     CUnorderedMap<uint64_t, const char *> strTableIdMapNewStr;
97 };
98 
99 class ChunkDecoder {
100 public:
101     explicit ChunkDecoder(char *mAddr, uint64_t fSize);
102 
~ChunkDecoder()103     ~ChunkDecoder()
104     {
105         auto &objInfoVec = rawHeapArgs.rawObjInfoVec;
106         for (auto obj : objInfoVec) {
107             delete obj;
108         }
109     }
110 
GetRawHeapInfoArgs()111     RawHeapInfoArgs &GetRawHeapInfoArgs()
112     {
113         return rawHeapArgs;
114     }
115 
116 private:
117     void DecodeStrTable(const char *charPtr);
118 
119     uint64_t heapObjCnt;
120     uint64_t rootObjCnt;
121     uint64_t shareObjCnt;
122     uint64_t strTableOffset;
123     RawHeapInfoArgs rawHeapArgs;
124     char *mapAddr;
125     uint64_t fileSize;
126 };
127 
128 class HeapProfiler : public HeapProfilerInterface {
129 public:
130     NO_MOVE_SEMANTIC(HeapProfiler);
131     NO_COPY_SEMANTIC(HeapProfiler);
132     explicit HeapProfiler(const EcmaVM *vm);
133     ~HeapProfiler() override;
134 
135     enum class SampleType { ONE_SHOT, REAL_TIME };
136 
137     void AllocationEvent(TaggedObject *address, size_t size) override;
138     void MoveEvent(uintptr_t address, TaggedObject *forwardAddress, size_t size) override;
139     /**
140      * dump the specific snapshot in target format
141      */
142     bool DumpHeapSnapshot(Stream *stream, const DumpSnapShotOption &dumpOption, Progress *progress = nullptr) override;
143     void DumpHeapSnapshot(const DumpSnapShotOption &dumpOption) override;
144     void AddSnapshot(HeapSnapshot *snapshot);
145 
146     bool StartHeapTracking(double timeInterval, bool isVmMode = true, Stream *stream = nullptr,
147                            bool traceAllocation = false, bool newThread = true) override;
148     bool StopHeapTracking(Stream *stream, Progress *progress = nullptr, bool newThread = true) override;
149     bool UpdateHeapTracking(Stream *stream) override;
150     bool StartHeapSampling(uint64_t samplingInterval, int stackDepth = 128) override;
151     void StopHeapSampling() override;
152     const struct SamplingInfo *GetAllocationProfile() override;
153     size_t GetIdCount() override
154     {
155         return entryIdMap_->GetIdCount();
156     }
GetEntryIdMap() const157     EntryIdMap *GetEntryIdMap() const
158     {
159         return const_cast<EntryIdMap *>(entryIdMap_);
160     }
GetChunk() const161     Chunk *GetChunk() const
162     {
163         return const_cast<Chunk *>(&chunk_);
164     }
GetEcmaStringTable() const165     StringHashMap *GetEcmaStringTable() const
166     {
167         return const_cast<StringHashMap *>(&stringTable_);
168     }
169     bool GenerateHeapSnapshot(std::string &inputFilePath, std::string &outputPath) override;
170 
171 private:
172     /**
173      * trigger full gc to make sure no unreachable objects in heap
174      */
175     bool ForceFullGC(const EcmaVM *vm);
176     void ForceSharedGC();
177 
178     /**
179      * make a new heap snapshot and put it into a container eg, vector
180      */
181     HeapSnapshot *MakeHeapSnapshot(SampleType sampleType, const DumpSnapShotOption &dumpOption,
182                                    bool traceAllocation = false);
183     bool DoDump(Stream *stream, Progress *progress, const DumpSnapShotOption &dumpOption);
184     std::string GenDumpFileName(DumpFormat dumpFormat);
185     CString GetTimeStamp();
186     void UpdateHeapObjects(HeapSnapshot *snapshot);
187     void ClearSnapshot();
188     void FillIdMap();
189     bool BinaryDump(Stream *stream, const DumpSnapShotOption &dumpOption);
190 
191     const size_t MAX_NUM_HPROF = 5;  // ~10MB
192     const EcmaVM *vm_;
193     CVector<HeapSnapshot *> hprofs_;
194     StringHashMap stringTable_;
195     bool isProfiling_ {false};
196     EntryIdMap* entryIdMap_;
197     std::unique_ptr<HeapTracker> heapTracker_;
198     Chunk chunk_;
199     std::unique_ptr<HeapSampling> heapSampling_ {nullptr};
200     Mutex mutex_;
201 };
202 }  // namespace panda::ecmascript
203 #endif  // ECMASCRIPT_DFX_HPROF_HEAP_PROFILER_H
204