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_SNAPSHOT_H
17#define ECMASCRIPT_DFX_HPROF_HEAP_SNAPSHOT_H
18
19#include <atomic>
20#include <cstdint>
21#include <fstream>
22#include <sys/time.h>
23
24#include "ecmascript/dfx/hprof/heap_profiler.h"
25#include "ecmascript/dfx/hprof/heap_root_visitor.h"
26#include "ecmascript/dfx/hprof/string_hashmap.h"
27#include "ecmascript/js_hclass.h"
28#include "ecmascript/js_object.h"
29#include "ecmascript/js_tagged_value.h"
30#include "ecmascript/jspandafile/method_literal.h"
31#include "ecmascript/mem/c_containers.h"
32#include "ecmascript/dfx/hprof/file_stream.h"
33#include "ecmascript/interpreter/frame_handler.h"
34#include "ecmascript/mem/object_xray.h"
35#include "ecmascript/object_fast_operator.h"
36
37namespace panda::ecmascript {
38class EntryIdMap;
39// Define the Object Graphic
40using Address = uintptr_t;
41
42enum class NodeType {
43    HIDDEN,
44    ARRAY,
45    STRING,
46    OBJECT,
47    CODE,
48    CLOSURE,
49    REGEXP,
50    HEAPNUMBER,
51    NATIVE,
52    SYNTHETIC,
53    CONSSTRING,
54    SLICEDSTRING,
55    SYMBOL,
56    BIGINT,
57    DEFAULT = NATIVE,
58};
59
60enum class EdgeType { CONTEXT, ELEMENT, PROPERTY, INTERNAL, HIDDEN, SHORTCUT, WEAK, DEFAULT = PROPERTY };
61
62class Node {
63public:
64    Node(NodeId id, uint32_t index, const CString *name, NodeType type, size_t size, size_t nativeSize,
65         uint32_t traceId, JSTaggedType address, bool isLive = true)
66        : id_(id),
67          index_(index),
68          name_(name),
69          type_(type),
70          size_(size),
71          nativeSize_(nativeSize),
72          traceId_(traceId),
73          address_(address),
74          isLive_(isLive)
75    {
76    }
77    NodeId GetId() const
78    {
79        return id_;
80    }
81    void SetIndex(uint32_t index)
82    {
83        index_ = index;
84    }
85    uint32_t GetIndex() const
86    {
87        return index_;
88    }
89
90    const CString *GetName() const
91    {
92        return name_;
93    }
94
95    void SetName(CString *name)
96    {
97        name_ = name;
98    }
99
100    NodeType GetType() const
101    {
102        return type_;
103    }
104    size_t GetSelfSize() const
105    {
106        return size_;
107    }
108    void SetSelfSize(size_t size)
109    {
110        size_ = size;
111    }
112    size_t GetNativeSize() const
113    {
114        return nativeSize_;
115    }
116    void SetNativeSize(size_t size)
117    {
118        nativeSize_ = size;
119    }
120    size_t GetEdgeCount() const
121    {
122        return edgeCount_;
123    }
124    void IncEdgeCount()
125    {
126        edgeCount_++;
127    }
128    uint32_t GetStackTraceId() const
129    {
130        return traceId_;
131    }
132    JSTaggedType GetAddress() const
133    {
134        return address_;
135    }
136    void SetAddress(JSTaggedType address)
137    {
138        address_ = address;
139    }
140    bool IsLive() const
141    {
142        return isLive_;
143    }
144    void SetLive(bool isLive)
145    {
146        isLive_ = isLive;
147    }
148    void SetTraceId(uint32_t traceId)
149    {
150        traceId_ = traceId;
151    }
152    static Node *NewNode(Chunk *chunk, NodeId id, size_t index, const CString *name, NodeType type, size_t size,
153                         size_t nativeSize, JSTaggedType entry, bool isLive = true);
154    template<typename T>
155    static JSTaggedType NewAddress(T *addr)
156    {
157        return reinterpret_cast<JSTaggedType>(addr);
158    }
159    static constexpr int NODE_FIELD_COUNT = 8;
160    ~Node() = default;
161
162private:
163    NodeId id_ {0};  // Range from 1
164    uint32_t index_ {0};
165    const CString *name_ {nullptr};
166    NodeType type_ {NodeType::DEFAULT};
167    size_t size_ {0};
168    size_t nativeSize_ {0};
169    size_t edgeCount_ {0};
170    uint32_t traceId_ {0};
171    JSTaggedType address_ {0};
172    bool isLive_ {true};
173};
174
175class Edge {
176public:
177    Edge(EdgeType type, Node *from, Node *to, CString *name)
178        : edgeType_(type), from_(from), to_(to), name_(name) {}
179    Edge(EdgeType type, Node *from, Node *to, uint32_t index)
180        : edgeType_(type), from_(from), to_(to), index_(index) {}
181    EdgeType GetType() const
182    {
183        return edgeType_;
184    }
185    const Node *GetFrom() const
186    {
187        return from_;
188    }
189    const Node *GetTo() const
190    {
191        return to_;
192    }
193    const CString *GetName() const
194    {
195        ASSERT(GetType() != EdgeType::ELEMENT);
196        return name_;
197    }
198    uint32_t GetIndex() const
199    {
200        ASSERT(GetType() == EdgeType::ELEMENT);
201        return index_;
202    }
203    void SetName(CString *name)
204    {
205        ASSERT(GetType() != EdgeType::ELEMENT);
206        name_ = name;
207    }
208    void UpdateFrom(Node *node)
209    {
210        from_ = node;
211    }
212    void UpdateTo(Node *node)
213    {
214        to_ = node;
215    }
216    static Edge *NewEdge(Chunk *chunk, EdgeType type, Node *from, Node *to, CString *name);
217    static Edge *NewEdge(Chunk *chunk, EdgeType type, Node *from, Node *to, uint32_t index);
218    static constexpr int EDGE_FIELD_COUNT = 3;
219    ~Edge() = default;
220
221private:
222    EdgeType edgeType_ {EdgeType::DEFAULT};
223    Node *from_ {nullptr};
224    Node *to_ {nullptr};
225    union {
226        CString *name_;
227        uint32_t index_;
228    };
229};
230
231class TimeStamp {
232public:
233    explicit TimeStamp(int sequenceId) : lastSequenceId_(sequenceId), timeStampUs_(TimeStamp::Now()) {}
234    ~TimeStamp() = default;
235
236    DEFAULT_MOVE_SEMANTIC(TimeStamp);
237    DEFAULT_COPY_SEMANTIC(TimeStamp);
238
239    int GetLastSequenceId() const
240    {
241        return lastSequenceId_;
242    }
243
244    int64_t GetTimeStamp() const
245    {
246        return timeStampUs_;
247    }
248
249    uint32_t GetSize() const
250    {
251        return size_;
252    }
253
254    void SetSize(uint32_t size)
255    {
256        size_ = size;
257    }
258
259    uint32_t GetCount() const
260    {
261        return count_;
262    }
263
264    void SetCount(uint32_t count)
265    {
266        count_ = count;
267    }
268
269private:
270    static int64_t Now()
271    {
272        struct timeval tv = {0, 0};
273        gettimeofday(&tv, nullptr);
274        const int THOUSAND = 1000;
275        return tv.tv_usec + tv.tv_sec * THOUSAND * THOUSAND;
276    }
277
278    int lastSequenceId_ {0};
279    int64_t timeStampUs_ {0};
280    uint32_t size_ {0};
281    uint32_t count_ {0};
282};
283
284class HeapEntryMap {
285public:
286    HeapEntryMap() = default;
287    ~HeapEntryMap() = default;
288    NO_MOVE_SEMANTIC(HeapEntryMap);
289    NO_COPY_SEMANTIC(HeapEntryMap);
290    Node *FindOrInsertNode(Node *node);
291    Node *FindAndEraseNode(JSTaggedType addr);
292    Node *FindEntry(JSTaggedType addr);
293    void InsertEntry(Node *node);
294
295private:
296    CUnorderedMap<JSTaggedType, Node *> nodesMap_ {};
297};
298
299struct FunctionInfo {
300    int functionId = 0;
301    std::string functionName = "";
302    std::string scriptName = "";
303    int scriptId = 0;
304    int columnNumber = 0;
305    int lineNumber = 0;
306};
307
308class TraceTree;
309class TraceNode {
310public:
311    TraceNode(TraceTree* tree, uint32_t nodeIndex);
312    ~TraceNode();
313
314    TraceNode(const TraceNode&) = delete;
315    TraceNode& operator=(const TraceNode&) = delete;
316    TraceNode* FindChild(uint32_t nodeIndex);
317    TraceNode* FindOrAddChild(uint32_t nodeIndex);
318    uint32_t GetNodeIndex() const
319    {
320        return nodeIndex_;
321    }
322    uint32_t GetTotalSize() const
323    {
324        return totalSize_;
325    }
326    uint32_t GetTotalCount() const
327    {
328        return totalCount_;
329    }
330    uint32_t GetId() const
331    {
332        return id_;
333    }
334    const std::vector<TraceNode*>& GetChildren() const
335    {
336        return children_;
337    }
338    TraceNode &SetTotalSize(uint32_t totalSize)
339    {
340        totalSize_ = totalSize;
341        return *this;
342    }
343    TraceNode &SetTotalCount(uint32_t totalCount)
344    {
345        totalCount_ = totalCount;
346        return *this;
347    }
348
349private:
350    TraceTree* tree_ {nullptr};
351    uint32_t nodeIndex_ {0};
352    uint32_t totalSize_ {0};
353    uint32_t totalCount_ {0};
354    uint32_t id_ {0};
355    std::vector<TraceNode*> children_ {};
356};
357
358class TraceTree {
359public:
360    TraceTree() : nextNodeId_(1), root_(this, 0)
361    {
362    }
363    ~TraceTree() = default;
364    TraceTree(const TraceTree&) = delete;
365    TraceTree& operator=(const TraceTree&) = delete;
366    TraceNode* AddNodeToTree(CVector<uint32_t> traceNodeIndex);
367    TraceNode* GetRoot()
368    {
369        return &root_;
370    }
371    uint32_t GetNextNodeId()
372    {
373        return nextNodeId_++;
374    }
375
376private:
377    uint32_t nextNodeId_ {0};
378    TraceNode root_;
379};
380
381struct Reference {
382    enum class ReferenceType { CONTEXT, ELEMENT, PROPERTY, INTERNAL, HIDDEN, SHORTCUT, WEAK, DEFAULT = PROPERTY };
383
384    Reference(const CString &name, JSTaggedValue value) : name_(name), value_(value) {}
385    Reference(const CString &name, JSTaggedValue value, ReferenceType type) : name_(name), value_(value), type_(type) {}
386    Reference(uint32_t index, JSTaggedValue value, ReferenceType type) : index_(index), value_(value), type_(type) {}
387
388    CString name_;
389    uint32_t index_ {-1U};
390    JSTaggedValue value_;
391    ReferenceType type_ {ReferenceType::DEFAULT};
392};
393
394class EntryVisitor {
395public:
396    NO_MOVE_SEMANTIC(EntryVisitor);
397    NO_COPY_SEMANTIC(EntryVisitor);
398    EntryVisitor() = default;
399    ~EntryVisitor() = default;
400    static CString ConvertKey(JSTaggedValue key);
401};
402
403class HeapSnapshot {
404public:
405    static constexpr int SEQ_STEP = 2;
406    NO_MOVE_SEMANTIC(HeapSnapshot);
407    NO_COPY_SEMANTIC(HeapSnapshot);
408    HeapSnapshot(const EcmaVM *vm, StringHashMap *stringTable, const DumpSnapShotOption &dumpOption,
409                 const bool trackAllocations, EntryIdMap *entryIdMap, Chunk *chunk)
410        : vm_(vm), stringTable_(stringTable), isVmMode_(dumpOption.isVmMode), isPrivate_(dumpOption.isPrivate),
411          captureNumericValue_(dumpOption.captureNumericValue), trackAllocations_(trackAllocations),
412          entryIdMap_(entryIdMap), chunk_(chunk) {}
413    ~HeapSnapshot();
414    bool BuildUp(bool isSimplify = false);
415    bool Verify();
416
417    void PrepareSnapshot();
418    void UpdateNodes(bool isInFinish = false);
419    Node *AddNode(TaggedObject *address, size_t size);
420    void MoveNode(uintptr_t address, TaggedObject *forwardAddress, size_t size);
421    void RecordSampleTime();
422    bool FinishSnapshot();
423    void PushHeapStat(Stream* stream);
424    int AddTraceNode(int sequenceId, int size);
425    bool AddMethodInfo(MethodLiteral *methodLiteral,
426                       const JSPandaFile *jsPandaFile, int sequenceId);
427    void AddTraceNodeId(MethodLiteral *methodLiteral);
428
429    const CVector<TimeStamp> &GetTimeStamps() const
430    {
431        return timeStamps_;
432    }
433
434    size_t GetNodeCount() const
435    {
436        return nodeCount_;
437    }
438    size_t GetEdgeCount() const
439    {
440        return edgeCount_;
441    }
442    size_t GetTotalNodeSize() const
443    {
444        return totalNodesSize_;
445    }
446    void AccumulateNodeSize(size_t size)
447    {
448        totalNodesSize_ += static_cast<int>(size);
449    }
450    void DecreaseNodeSize(size_t size)
451    {
452        totalNodesSize_ -= static_cast<int>(size);
453    }
454    CString *GenerateNodeName(TaggedObject *entry);
455    NodeType GenerateNodeType(TaggedObject *entry);
456    const CList<Node *> *GetNodes() const
457    {
458        return &nodes_;
459    }
460    const CList<Edge *> *GetEdges() const
461    {
462        return &edges_;
463    }
464
465    CString *GetString(const CString &as);
466    CString *GetArrayString(TaggedArray *array, const CString &as);
467
468    bool IsInVmMode() const
469    {
470        return isVmMode_;
471    }
472
473    bool IsPrivate() const
474    {
475        return isPrivate_;
476    }
477
478    bool trackAllocations() const
479    {
480        return trackAllocations_;
481    }
482
483    const CVector<FunctionInfo> &GetTrackAllocationsStack() const
484    {
485        return traceInfoStack_;
486    }
487
488    TraceTree* GetTraceTree()
489    {
490        return &traceTree_;
491    }
492
493    void PrepareTraceInfo()
494    {
495        struct FunctionInfo info;
496        info.functionName = "(root)";
497        GetString(info.functionName.c_str());
498        traceInfoStack_.push_back(info);
499    }
500    const StringHashMap *GetEcmaStringTable() const
501    {
502        return stringTable_;
503    }
504
505    bool BuildSnapshotForBinMod(CVector<RawHeapObjInfo *> &objInfoVec);
506    Node *GenerateNodeForBinMod(TaggedObject *obj, RawHeapObjInfo *objInfo,
507                                CUnorderedMap<uint64_t, const char *> &strTableIdMap);
508
509    StringId GenerateStringId(TaggedObject *obj)
510    {
511        JSTaggedValue entry(obj);
512        if (entry.IsOnlyJSObject()) {
513            return stringTable_->InsertStrAndGetStringId(ParseObjectName(obj));
514        }
515        if (entry.IsJSFunction()) {
516            return stringTable_->InsertStrAndGetStringId(ParseFunctionName(obj));
517        }
518        return 1; // 1 : invalid id
519    }
520
521private:
522    void FillNodes(bool isInFinish = false, bool isSimplify = false);
523    Node *GenerateNode(JSTaggedValue entry, size_t size = 0,
524                       bool isInFinish = false, bool isSimplify = false, bool isBinMod = false);
525    Node *HandleStringNode(JSTaggedValue &entry, size_t &size, bool &isInFinish, bool isBinMod);
526    Node *HandleFunctionNode(JSTaggedValue &entry, size_t &size, bool &isInFinish);
527    Node *HandleObjectNode(JSTaggedValue &entry, size_t &size, bool &isInFinish);
528    Node *HandleBaseClassNode(size_t size, bool idExist, NodeId &sequenceId,
529                              TaggedObject* obj, JSTaggedType &addr);
530    CString GeneratePrimitiveNameString(JSTaggedValue &entry);
531    Node *GeneratePrivateStringNode(size_t size);
532    Node *GenerateStringNode(JSTaggedValue entry, size_t size, bool isInFinish = false, bool isBinMod = false);
533    Node *GenerateFunctionNode(JSTaggedValue entry, size_t size, bool isInFinish = false);
534    Node *GenerateObjectNode(JSTaggedValue entry, size_t size, bool isInFinish = false);
535    void FillEdges(bool isSimplify = false);
536    void RenameFunction(const CString &edgeName, Node *entryFrom, Node *entryTo);
537    CString ParseFunctionName(TaggedObject *obj);
538    const CString ParseObjectName(TaggedObject *obj);
539
540    Node *InsertNodeUnique(Node *node);
541    void EraseNodeUnique(Node *node);
542    Edge *InsertEdgeUnique(Edge *edge);
543    void AddSyntheticRoot();
544    void FillEdgesForBinMod(RawHeapObjInfo *objInfo);
545    void AddSyntheticRootForBinMod(RawHeapObjInfo *objInfoVec, int &edgeOffset, Node *syntheticRoot);
546    Node *InsertNodeAt(size_t pos, Node *node);
547    Edge *InsertEdgeAt(size_t pos, Edge *edge);
548
549    CList<Node *> nodes_ {};
550    CList<Edge *> edges_ {};
551    CVector<TimeStamp> timeStamps_ {};
552    int nodeCount_ {0};
553    int edgeCount_ {0};
554    int totalNodesSize_ {0};
555    HeapEntryMap entryMap_;
556    panda::ecmascript::HeapRootVisitor rootVisitor_;
557    const EcmaVM *vm_;
558    StringHashMap *stringTable_ {nullptr};
559    bool isVmMode_ {true};
560    bool isPrivate_ {false};
561    bool captureNumericValue_ {false};
562    Node* privateStringNode_ {nullptr};
563    bool trackAllocations_ {false};
564    CVector<FunctionInfo> traceInfoStack_ {};
565    CMap<MethodLiteral *, struct FunctionInfo> stackInfo_;
566    CMap<std::string, int> scriptIdMap_;
567    TraceTree traceTree_;
568    CMap<MethodLiteral *, uint32_t> methodToTraceNodeId_;
569    CVector<uint32_t> traceNodeIndex_;
570    EntryIdMap* entryIdMap_;
571    Chunk *chunk_ {nullptr};
572};
573
574}  // namespace panda::ecmascript
575#endif  // ECMASCRIPT_DFX_HPROF_HEAP_SNAPSHOT_H
576