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