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