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