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