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