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