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
29namespace panda::ecmascript {
30class HeapSnapshot;
31class EcmaVM;
32using NodeId = uint64_t;
33
34class EntryIdMap {
35public:
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);
47    NodeId GetNextId()
48    {
49        nextId_ += SEQ_STEP;
50        return nextId_ - SEQ_STEP;
51    }
52    NodeId GetLastId()
53    {
54        return nextId_ - SEQ_STEP;
55    }
56    size_t GetIdCount()
57    {
58        return idMap_.size();
59    }
60    CUnorderedMap<JSTaggedType, NodeId>* GetIdMap()
61    {
62        return &idMap_;
63    }
64    NodeId GetId()
65    {
66        return nextId_;
67    }
68    void SetId(NodeId id)
69    {
70        nextId_ = id;
71    }
72
73private:
74    NodeId nextId_ {3U};  // 1 Reversed for SyntheticRoot
75    CUnorderedMap<JSTaggedType, NodeId> idMap_ {};
76};
77
78struct 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
86struct RawHeapObjInfo {
87    AddrTableItem *tInfo;
88    char *newAddr;
89    bool isRoot;
90    CUnorderedSet<uint64_t> refSet;
91};
92
93struct RawHeapInfoArgs {
94    CVector<RawHeapObjInfo *> rawObjInfoVec;
95    CUnorderedMap<uint64_t, RawHeapObjInfo *> oldAddrMapObjInfo;
96    CUnorderedMap<uint64_t, const char *> strTableIdMapNewStr;
97};
98
99class ChunkDecoder {
100public:
101    explicit ChunkDecoder(char *mAddr, uint64_t fSize);
102
103    ~ChunkDecoder()
104    {
105        auto &objInfoVec = rawHeapArgs.rawObjInfoVec;
106        for (auto obj : objInfoVec) {
107            delete obj;
108        }
109    }
110
111    RawHeapInfoArgs &GetRawHeapInfoArgs()
112    {
113        return rawHeapArgs;
114    }
115
116private:
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
128class HeapProfiler : public HeapProfilerInterface {
129public:
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    }
157    EntryIdMap *GetEntryIdMap() const
158    {
159        return const_cast<EntryIdMap *>(entryIdMap_);
160    }
161    Chunk *GetChunk() const
162    {
163        return const_cast<Chunk *>(&chunk_);
164    }
165    StringHashMap *GetEcmaStringTable() const
166    {
167        return const_cast<StringHashMap *>(&stringTable_);
168    }
169    bool GenerateHeapSnapshot(std::string &inputFilePath, std::string &outputPath) override;
170
171private:
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