• 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     DEFAULT = NATIVE,
58 };
59 
60 enum class EdgeType { CONTEXT, ELEMENT, PROPERTY, INTERNAL, HIDDEN, SHORTCUT, WEAK, DEFAULT = PROPERTY };
61 
62 class Node {
63 public:
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)
id_(id)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     }
GetId()77     NodeId GetId() const
78     {
79         return id_;
80     }
SetIndex(uint32_t index)81     void SetIndex(uint32_t index)
82     {
83         index_ = index;
84     }
GetIndex()85     uint32_t GetIndex() const
86     {
87         return index_;
88     }
89 
GetName()90     const CString *GetName() const
91     {
92         return name_;
93     }
94 
SetName(CString * name)95     void SetName(CString *name)
96     {
97         name_ = name;
98     }
99 
GetType()100     NodeType GetType() const
101     {
102         return type_;
103     }
GetSelfSize()104     size_t GetSelfSize() const
105     {
106         return size_;
107     }
SetSelfSize(size_t size)108     void SetSelfSize(size_t size)
109     {
110         size_ = size;
111     }
GetNativeSize()112     size_t GetNativeSize() const
113     {
114         return nativeSize_;
115     }
SetNativeSize(size_t size)116     void SetNativeSize(size_t size)
117     {
118         nativeSize_ = size;
119     }
GetEdgeCount()120     size_t GetEdgeCount() const
121     {
122         return edgeCount_;
123     }
IncEdgeCount()124     void IncEdgeCount()
125     {
126         edgeCount_++;
127     }
GetStackTraceId()128     uint32_t GetStackTraceId() const
129     {
130         return traceId_;
131     }
GetAddress()132     JSTaggedType GetAddress() const
133     {
134         return address_;
135     }
SetAddress(JSTaggedType address)136     void SetAddress(JSTaggedType address)
137     {
138         address_ = address;
139     }
IsLive()140     bool IsLive() const
141     {
142         return isLive_;
143     }
SetLive(bool isLive)144     void SetLive(bool isLive)
145     {
146         isLive_ = isLive;
147     }
SetTraceId(uint32_t traceId)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>
NewAddress(T * addr)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 
162 private:
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 
175 class Edge {
176 public:
Edge(EdgeType type,Node * from,Node * to,CString * name)177     Edge(EdgeType type, Node *from, Node *to, CString *name)
178         : edgeType_(type), from_(from), to_(to), name_(name) {}
Edge(EdgeType type,Node * from,Node * to,uint32_t index)179     Edge(EdgeType type, Node *from, Node *to, uint32_t index)
180         : edgeType_(type), from_(from), to_(to), index_(index) {}
GetType()181     EdgeType GetType() const
182     {
183         return edgeType_;
184     }
GetFrom()185     const Node *GetFrom() const
186     {
187         return from_;
188     }
GetTo()189     const Node *GetTo() const
190     {
191         return to_;
192     }
GetName()193     const CString *GetName() const
194     {
195         ASSERT(GetType() != EdgeType::ELEMENT);
196         return name_;
197     }
GetIndex()198     uint32_t GetIndex() const
199     {
200         ASSERT(GetType() == EdgeType::ELEMENT);
201         return index_;
202     }
SetName(CString * name)203     void SetName(CString *name)
204     {
205         ASSERT(GetType() != EdgeType::ELEMENT);
206         name_ = name;
207     }
UpdateFrom(Node * node)208     void UpdateFrom(Node *node)
209     {
210         from_ = node;
211     }
UpdateTo(Node * node)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 
221 private:
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 
231 class TimeStamp {
232 public:
TimeStamp(int sequenceId)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 
GetLastSequenceId()239     int GetLastSequenceId() const
240     {
241         return lastSequenceId_;
242     }
243 
GetTimeStamp()244     int64_t GetTimeStamp() const
245     {
246         return timeStampUs_;
247     }
248 
GetSize()249     uint32_t GetSize() const
250     {
251         return size_;
252     }
253 
SetSize(uint32_t size)254     void SetSize(uint32_t size)
255     {
256         size_ = size;
257     }
258 
GetCount()259     uint32_t GetCount() const
260     {
261         return count_;
262     }
263 
SetCount(uint32_t count)264     void SetCount(uint32_t count)
265     {
266         count_ = count;
267     }
268 
269 private:
Now()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 
284 class HeapEntryMap {
285 public:
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 
295 private:
296     CUnorderedMap<JSTaggedType, Node *> nodesMap_ {};
297 };
298 
299 struct 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 
308 class TraceTree;
309 class TraceNode {
310 public:
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);
GetNodeIndex()318     uint32_t GetNodeIndex() const
319     {
320         return nodeIndex_;
321     }
GetTotalSize()322     uint32_t GetTotalSize() const
323     {
324         return totalSize_;
325     }
GetTotalCount()326     uint32_t GetTotalCount() const
327     {
328         return totalCount_;
329     }
GetId()330     uint32_t GetId() const
331     {
332         return id_;
333     }
GetChildren()334     const std::vector<TraceNode*>& GetChildren() const
335     {
336         return children_;
337     }
SetTotalSize(uint32_t totalSize)338     TraceNode &SetTotalSize(uint32_t totalSize)
339     {
340         totalSize_ = totalSize;
341         return *this;
342     }
SetTotalCount(uint32_t totalCount)343     TraceNode &SetTotalCount(uint32_t totalCount)
344     {
345         totalCount_ = totalCount;
346         return *this;
347     }
348 
349 private:
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 
358 class TraceTree {
359 public:
TraceTree()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);
GetRoot()367     TraceNode* GetRoot()
368     {
369         return &root_;
370     }
GetNextNodeId()371     uint32_t GetNextNodeId()
372     {
373         return nextNodeId_++;
374     }
375 
376 private:
377     uint32_t nextNodeId_ {0};
378     TraceNode root_;
379 };
380 
381 struct Reference {
382     enum class ReferenceType { CONTEXT, ELEMENT, PROPERTY, INTERNAL, HIDDEN, SHORTCUT, WEAK, DEFAULT = PROPERTY };
383 
ReferenceReference384     Reference(const CString &name, JSTaggedValue value) : name_(name), value_(value) {}
ReferenceReference385     Reference(const CString &name, JSTaggedValue value, ReferenceType type) : name_(name), value_(value), type_(type) {}
ReferenceReference386     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 
394 class EntryVisitor {
395 public:
396     NO_MOVE_SEMANTIC(EntryVisitor);
397     NO_COPY_SEMANTIC(EntryVisitor);
398     EntryVisitor() = default;
399     ~EntryVisitor() = default;
400     static CString ConvertKey(JSTaggedValue key);
401 };
402 
403 class HeapSnapshot {
404 public:
405     static constexpr int SEQ_STEP = 2;
406     NO_MOVE_SEMANTIC(HeapSnapshot);
407     NO_COPY_SEMANTIC(HeapSnapshot);
HeapSnapshot(const EcmaVM * vm,StringHashMap * stringTable,const DumpSnapShotOption & dumpOption,const bool trackAllocations,EntryIdMap * entryIdMap)408     HeapSnapshot(const EcmaVM *vm, StringHashMap *stringTable, const DumpSnapShotOption &dumpOption,
409                  const bool trackAllocations, EntryIdMap *entryIdMap)
410         : vm_(vm), stringTable_(stringTable), isVmMode_(dumpOption.isVmMode), isPrivate_(dumpOption.isPrivate),
411           captureNumericValue_(dumpOption.captureNumericValue), trackAllocations_(trackAllocations),
412           entryIdMap_(entryIdMap), chunk_(vm->GetNativeAreaAllocator()) {}
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 
GetTimeStamps()429     const CVector<TimeStamp> &GetTimeStamps() const
430     {
431         return timeStamps_;
432     }
433 
GetNodeCount()434     size_t GetNodeCount() const
435     {
436         return nodeCount_;
437     }
GetEdgeCount()438     size_t GetEdgeCount() const
439     {
440         return edgeCount_;
441     }
GetTotalNodeSize()442     size_t GetTotalNodeSize() const
443     {
444         return totalNodesSize_;
445     }
AccumulateNodeSize(size_t size)446     void AccumulateNodeSize(size_t size)
447     {
448         totalNodesSize_ += size;
449     }
DecreaseNodeSize(size_t size)450     void DecreaseNodeSize(size_t size)
451     {
452         totalNodesSize_ -= size;
453     }
454     CString *GenerateNodeName(TaggedObject *entry);
455     NodeType GenerateNodeType(TaggedObject *entry);
GetNodes()456     const CList<Node *> *GetNodes() const
457     {
458         return &nodes_;
459     }
GetEdges()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 
IsInVmMode()468     bool IsInVmMode() const
469     {
470         return isVmMode_;
471     }
472 
IsPrivate()473     bool IsPrivate() const
474     {
475         return isPrivate_;
476     }
477 
trackAllocations()478     bool trackAllocations() const
479     {
480         return trackAllocations_;
481     }
482 
GetTrackAllocationsStack()483     const CVector<FunctionInfo> &GetTrackAllocationsStack() const
484     {
485         return traceInfoStack_;
486     }
487 
GetTraceTree()488     TraceTree* GetTraceTree()
489     {
490         return &traceTree_;
491     }
492 
PrepareTraceInfo()493     void PrepareTraceInfo()
494     {
495         struct FunctionInfo info;
496         info.functionName = "(root)";
497         GetString(info.functionName.c_str());
498         traceInfoStack_.push_back(info);
499     }
GetEcmaStringTable()500     const StringHashMap *GetEcmaStringTable() const
501     {
502         return stringTable_;
503     }
504 
505     bool BuildSnapshotForBinMod(CUnorderedMap<uint64_t, NewAddr *> &objMap,
506                                 CUnorderedMap<uint64_t, CUnorderedSet<uint64_t>> &refSetMap);
507     void GenerateNodeForBinMod(CUnorderedMap<uint64_t, NewAddr *> &objMap, CUnorderedSet<uint64_t> &rootSetMap,
508                                CUnorderedMap<uint64_t, CString *> &strTableIdMap);
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 FillEdgesForBinMod(char *newAddr, CUnorderedSet<uint64_t> *refSet);
546     Node *InsertNodeAt(size_t pos, Node *node);
547     Edge *InsertEdgeAt(size_t pos, Edge *edge);
548 
549 #if defined(ENABLE_LOCAL_HANDLE_LEAK_DETECT)
550     void LogLeakedLocalHandleBackTrace(ObjectSlot slot);
551 #endif
552 
553     CList<Node *> nodes_ {};
554     CList<Edge *> edges_ {};
555     CVector<TimeStamp> timeStamps_ {};
556     uint32_t nodeCount_ {0};
557     uint32_t edgeCount_ {0};
558     size_t totalNodesSize_ {0};
559     HeapEntryMap entryMap_;
560     panda::ecmascript::HeapRootVisitor rootVisitor_;
561     const EcmaVM *vm_;
562     StringHashMap *stringTable_ {nullptr};
563     bool isVmMode_ {true};
564     bool isPrivate_ {false};
565     bool captureNumericValue_ {false};
566     Node* privateStringNode_ {nullptr};
567     bool trackAllocations_ {false};
568     CVector<FunctionInfo> traceInfoStack_ {};
569     CMap<MethodLiteral *, struct FunctionInfo> stackInfo_;
570     CMap<std::string, int> scriptIdMap_;
571     TraceTree traceTree_;
572     CMap<MethodLiteral *, uint32_t> methodToTraceNodeId_;
573     CVector<uint32_t> traceNodeIndex_;
574     EntryIdMap* entryIdMap_;
575     Chunk chunk_;
576     friend class HeapSnapShotFriendTest;
577 };
578 
579 }  // namespace panda::ecmascript
580 #endif  // ECMASCRIPT_DFX_HPROF_HEAP_SNAPSHOT_H
581