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