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