• 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 #include <sys/time.h>
23 
24 #include "ecmascript/mem/c_containers.h"
25 #include "os/mem.h"
26 #include "ecmascript/hprof/heap_profiler.h"
27 #include "ecmascript/hprof/heap_root_visitor.h"
28 #include "ecmascript/hprof/string_hashmap.h"
29 #include "ecmascript/js_hclass.h"
30 #include "ecmascript/js_object.h"
31 #include "ecmascript/js_tagged_value.h"
32 
33 namespace panda::ecmascript {
34 // Define the Object Graphic
35 using Address = uintptr_t;
36 
37 enum class NodeType : uint8_t {
38     JSTYPE_DECL,
39     PRIM_STRING, /* Primitive String */
40     PRIM_ARRAY,  /* Primitive Array */
41     SYNTHETIC    /* For Synthetic Root */
42 };
43 
44 enum class EdgeType { CONTEXT, ELEMENT, PROPERTY, INTERNAL, HIDDEN, SHORTCUT, WEAK, DEFAULT = PROPERTY };
45 
46 class Node {
47 public:
48     explicit Node(uint64_t id, uint64_t index, CString *name, NodeType type, size_t size, uint64_t traceId,
49                   Address address, bool isLive = true)
id_(id)50         : id_(id),
51           index_(index),
52           name_(name),
53           type_(type),
54           size_(size),
55           traceId_(traceId),
56           address_(address),
57           isLive_(isLive)
58     {
59     }
GetId()60     uint64_t GetId() const
61     {
62         return id_;
63     }
SetIndex(uint64_t index)64     void SetIndex(uint64_t index)
65     {
66         index_ = index;
67     }
GetIndex()68     uint64_t GetIndex() const
69     {
70         return index_;
71     }
72 
GetName()73     const CString *GetName() const
74     {
75         return name_;
76     }
GetType()77     NodeType GetType() const
78     {
79         return type_;
80     }
GetSelfSize()81     size_t GetSelfSize() const
82     {
83         return size_;
84     }
GetEdgeCount()85     size_t GetEdgeCount() const
86     {
87         return edgeCount_;
88     }
IncEdgeCount()89     void IncEdgeCount()
90     {
91         edgeCount_++;
92     }
GetStackTraceId()93     uint64_t GetStackTraceId() const
94     {
95         return traceId_;
96     }
GetAddress()97     Address GetAddress() const
98     {
99         return address_;
100     }
IsLive()101     bool IsLive() const
102     {
103         return isLive_;
104     }
SetLive(bool isLive)105     void SetLive(bool isLive)
106     {
107         isLive_ = isLive;
108     }
109     static Node *NewNode(const Heap *heap, size_t id, size_t index, CString *name, NodeType type, size_t size,
110                          TaggedObject *entry, bool isLive = true);
111     template<typename T>
NewAddress(T * addr)112     static Address NewAddress(T *addr)
113     {
114         return reinterpret_cast<Address>(addr);
115     }
116     static constexpr int NODE_FIELD_COUNT = 7;
117     ~Node() = default;
118 private:
119     uint64_t id_{0};  // Range from 1
120     uint64_t index_{0};
121     CString *name_{nullptr};
122     NodeType type_{NodeType::INVALID};
123     size_t size_{0};
124     size_t edgeCount_{0};
125     uint64_t traceId_{0};
126     Address address_{0x0};
127     bool isLive_{true};
128 };
129 
130 class Edge {
131 public:
Edge(uint64_t id,EdgeType type,Node * from,Node * to,CString * name)132     explicit Edge(uint64_t id, EdgeType type, Node *from, Node *to, CString *name)
133         : id_(id), edgeType_(type), from_(from), to_(to), name_(name)
134     {
135     }
GetId()136     uint64_t GetId() const
137     {
138         return id_;
139     }
GetType()140     EdgeType GetType() const
141     {
142         return edgeType_;
143     }
GetFrom()144     const Node *GetFrom() const
145     {
146         return from_;
147     }
GetTo()148     const Node *GetTo() const
149     {
150         return to_;
151     }
GetName()152     const CString *GetName() const
153     {
154         return name_;
155     }
SetName(CString * name)156     void SetName(CString *name)
157     {
158         name_ = name;
159     }
UpdateFrom(Node * node)160     void UpdateFrom(Node *node)
161     {
162         from_ = node;
163     }
UpdateTo(Node * node)164     void UpdateTo(Node *node)
165     {
166         to_ = node;
167     }
168     static Edge *NewEdge(const Heap *heap, uint64_t id, EdgeType type, Node *from, Node *to, CString *name);
169     static constexpr int EDGE_FIELD_COUNT = 3;
170     ~Edge() = default;
171 private:
172     uint64_t id_{-1ULL};
173     EdgeType edgeType_{EdgeType::DEFAULT};
174     Node *from_{nullptr};
175     Node *to_{nullptr};
176     CString *name_{nullptr};
177 };
178 
179 class TimeStamp {
180 public:
TimeStamp(int sequenceId)181     explicit TimeStamp(int sequenceId) : lastSequenceId_(sequenceId), timeStampUs_(TimeStamp::Now()) {}
182     ~TimeStamp() = default;
183 
184     DEFAULT_MOVE_SEMANTIC(TimeStamp);
185     DEFAULT_COPY_SEMANTIC(TimeStamp);
186 
GetLastSequenceId()187     int GetLastSequenceId() const
188     {
189         return lastSequenceId_;
190     }
191 
GetTimeStamp()192     int64_t GetTimeStamp() const
193     {
194         return timeStampUs_;
195     }
196 
197 private:
Now()198     static int64_t Now()
199     {
200         struct timeval tv = {0, 0};
201         gettimeofday(&tv, nullptr);
202         const int THOUSAND = 1000;
203         return tv.tv_usec + tv.tv_sec * THOUSAND * THOUSAND;
204     }
205 
206     int lastSequenceId_{0};
207     int64_t timeStampUs_{0};
208 };
209 
210 class HeapEntryMap {
211 public:
212     HeapEntryMap() = default;
213     ~HeapEntryMap() = default;
214     NO_MOVE_SEMANTIC(HeapEntryMap);
215     NO_COPY_SEMANTIC(HeapEntryMap);
216     Node *FindOrInsertNode(Node *node);
217     Node *FindAndEraseNode(Address addr);
218     Node *FindEntry(Address addr);
GetCapcity()219     size_t GetCapcity() const
220     {
221         return nodesMap_.size();
222     }
GetEntryCount()223     size_t GetEntryCount() const
224     {
225         return nodeEntryCount_;
226     }
227     void InsertEntry(Node *node);
228 
229 private:
230     size_t nodeEntryCount_{0};
231     CUnorderedMap<Address, Node *> nodesMap_{};
232 };
233 
234 class HeapSnapShot {
235 public:
236     static constexpr int SEQ_STEP = 2;
237     NO_MOVE_SEMANTIC(HeapSnapShot);
238     NO_COPY_SEMANTIC(HeapSnapShot);
HeapSnapShot(JSThread * thread,const Heap * heap,const bool isVmMode)239     explicit HeapSnapShot(JSThread *thread, const Heap *heap, const bool isVmMode)
240         : stringTable_(heap), thread_(thread), heap_(heap), isVmMode_(isVmMode)
241     {
242     }
243     ~HeapSnapShot();
244     bool BuildUp(JSThread *thread);
245     bool Verify();
246 
247     void PrepareSnapShot();
248     void UpdateNode();
249     void AddNode(uintptr_t address);
250     void MoveNode(uintptr_t address, uintptr_t forward_address);
251     void RecordSampleTime();
252     bool FinishSnapShot();
253 
GetTimeStamps()254     const CVector<TimeStamp> &GetTimeStamps() const
255     {
256         return timeStamps_;
257     }
258 
GetNodeCount()259     size_t GetNodeCount() const
260     {
261         return nodeCount_;
262     }
GetEdgeCount()263     size_t GetEdgeCount() const
264     {
265         return edgeCount_;
266     }
GetTotalNodeSize()267     size_t GetTotalNodeSize() const
268     {
269         return totalNodesSize_;
270     }
AccumulateNodeSize(size_t size)271     void AccumulateNodeSize(size_t size)
272     {
273         totalNodesSize_ += size;
274     }
DecreaseNodeSize(size_t size)275     void DecreaseNodeSize(size_t size)
276     {
277         totalNodesSize_ -= size;
278     }
279     CString *GenerateNodeName(JSThread *thread, TaggedObject *entry);
280     NodeType GenerateNodeType(TaggedObject *entry);
GetNodes()281     const CList<Node *> *GetNodes() const
282     {
283         return &nodes_;
284     }
GetEdges()285     const CVector<Edge *> *GetEdges() const
286     {
287         return &edges_;
288     }
GetEcmaStringTable()289     const StringHashMap *GetEcmaStringTable() const
290     {
291         return &stringTable_;
292     }
293 
294     CString *GetString(const CString &as);
295 
IsInVmMode()296     bool IsInVmMode() const
297     {
298         return isVmMode_;
299     }
300 
301 private:
302     void FillNodes(JSThread *thread);
303     Node *GenerateNode(JSThread *thread, JSTaggedValue entry, int sequenceId = -1);
304     Node *GenerateStringNode(JSTaggedValue entry, int sequenceId);
305     void FillEdges(JSThread *thread);
306     void BridgeAllReferences();
307     CString *GenerateEdgeName(TaggedObject *from, TaggedObject *to);
308 
309     Node *InsertNodeUnique(Node *node);
310     void EraseNodeUnique(Node *node);
311     Edge *InsertEdgeUnique(Edge *edge);
312     void AddSyntheticRoot(JSThread *thread);
313     Node *InsertNodeAt(size_t pos, Node *node);
314     Edge *InsertEdgeAt(size_t pos, Edge *edge);
315 
316     StringHashMap stringTable_;
317     CList<Node *> nodes_{};
318     CVector<Edge *> edges_{};
319     CVector<TimeStamp> timeStamps_{};
320     std::atomic_int sequenceId_{1};  // 1 Reversed for SyntheticRoot
321     int nodeCount_{0};
322     int edgeCount_{0};
323     int totalNodesSize_{0};
324     HeapEntryMap entryMap_;
325     panda::ecmascript::HeapRootVisitor rootVisitor_;
326     JSThread *thread_;
327     const Heap *heap_;
328     bool isVmMode_{true};
329 };
330 
331 class EntryVisitor {
332 public:
333     NO_MOVE_SEMANTIC(EntryVisitor);
334     NO_COPY_SEMANTIC(EntryVisitor);
335     explicit EntryVisitor() = default;
336     ~EntryVisitor() = default;
337     static CString ConvertKey(JSTaggedValue key);
338 };
339 
340 enum class FrontType {
341     HIDDEN,           /* kHidden */
342     ARRAY,            /* kArray */
343     STRING,           /* kString */
344     OBJECT,           /* kObject */
345     CODE,             /* kCode */
346     CLOSURE,          /* kClosure */
347     REGEXP,           /* kRegExp */
348     HEAPNUMBER,       /* kHeapNumber */
349     NATIVE,           /* kNative */
350     SYNTHETIC,        /* kSynthetic */
351     CONSSTRING,       /* kConsString */
352     SLICEDSTRING,     /* kSlicedString */
353     SYMBOL,           /* kSymbol */
354     BIGINT,           /* kBigInt */
355     DEFAULT = NATIVE, /* kDefault */
356 };
357 
358 class NodeTypeConverter {
359 public:
360     explicit NodeTypeConverter() = default;
361     ~NodeTypeConverter() = default;
362     NO_MOVE_SEMANTIC(NodeTypeConverter);
363     NO_COPY_SEMANTIC(NodeTypeConverter);
364     /*
365      * For Front-End to Show Statistics Correctly
366      */
367     static FrontType Convert(NodeType type);
368 };
369 }  // namespace panda::ecmascript
370 #endif  // ECMASCRIPT_HPROF_HEAP_SNAPSHOT_H
371