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 FRAMEWORK, 58 DEFAULT = NATIVE, 59 }; 60 61 enum class EdgeType { CONTEXT, ELEMENT, PROPERTY, INTERNAL, HIDDEN, SHORTCUT, WEAK, DEFAULT = PROPERTY }; 62 63 class Node { 64 public: 65 Node(NodeId id, uint32_t index, const CString *name, NodeType type, size_t size, size_t nativeSize, 66 uint32_t traceId, JSTaggedType address, bool isLive = true) id_(id)67 : id_(id), 68 index_(index), 69 name_(name), 70 type_(type), 71 size_(size), 72 nativeSize_(nativeSize), 73 traceId_(traceId), 74 address_(address), 75 isLive_(isLive) 76 { 77 } GetId()78 NodeId GetId() const 79 { 80 return id_; 81 } SetIndex(uint32_t index)82 void SetIndex(uint32_t index) 83 { 84 index_ = index; 85 } GetIndex()86 uint32_t GetIndex() const 87 { 88 return index_; 89 } 90 GetName()91 const CString *GetName() const 92 { 93 return name_; 94 } 95 SetName(CString * name)96 void SetName(CString *name) 97 { 98 name_ = name; 99 } 100 GetType()101 NodeType GetType() const 102 { 103 return type_; 104 } SetType(NodeType type)105 void SetType(NodeType type) 106 { 107 type_ = type; 108 } GetSelfSize()109 size_t GetSelfSize() const 110 { 111 return size_; 112 } SetSelfSize(size_t size)113 void SetSelfSize(size_t size) 114 { 115 size_ = size; 116 } GetNativeSize()117 size_t GetNativeSize() const 118 { 119 return nativeSize_; 120 } SetNativeSize(size_t size)121 void SetNativeSize(size_t size) 122 { 123 nativeSize_ = size; 124 } GetEdgeCount()125 size_t GetEdgeCount() const 126 { 127 return edgeCount_; 128 } IncEdgeCount()129 void IncEdgeCount() 130 { 131 edgeCount_++; 132 } GetStackTraceId()133 uint32_t GetStackTraceId() const 134 { 135 return traceId_; 136 } GetAddress()137 JSTaggedType GetAddress() const 138 { 139 return address_; 140 } SetAddress(JSTaggedType address)141 void SetAddress(JSTaggedType address) 142 { 143 address_ = address; 144 } IsLive()145 bool IsLive() const 146 { 147 return isLive_; 148 } SetLive(bool isLive)149 void SetLive(bool isLive) 150 { 151 isLive_ = isLive; 152 } SetTraceId(uint32_t traceId)153 void SetTraceId(uint32_t traceId) 154 { 155 traceId_ = traceId; 156 } 157 static Node *NewNode(Chunk &chunk, NodeId id, size_t index, const CString *name, NodeType type, size_t size, 158 size_t nativeSize, JSTaggedType entry, bool isLive = true); 159 template<typename T> NewAddress(T * addr)160 static JSTaggedType NewAddress(T *addr) 161 { 162 return reinterpret_cast<JSTaggedType>(addr); 163 } 164 static constexpr int NODE_FIELD_COUNT = 8; 165 ~Node() = default; 166 167 private: 168 NodeId id_ {0}; // Range from 1 169 uint32_t index_ {0}; 170 const CString *name_ {nullptr}; 171 NodeType type_ {NodeType::DEFAULT}; 172 size_t size_ {0}; 173 size_t nativeSize_ {0}; 174 size_t edgeCount_ {0}; 175 uint32_t traceId_ {0}; 176 JSTaggedType address_ {0}; 177 bool isLive_ {true}; 178 }; 179 180 class Edge { 181 public: Edge(EdgeType type,Node * from,Node * to,CString * name)182 Edge(EdgeType type, Node *from, Node *to, CString *name) 183 : edgeType_(type), from_(from), to_(to), name_(name) {} Edge(EdgeType type,Node * from,Node * to,uint32_t index)184 Edge(EdgeType type, Node *from, Node *to, uint32_t index) 185 : edgeType_(type), from_(from), to_(to), index_(index) {} GetType()186 EdgeType GetType() const 187 { 188 return edgeType_; 189 } GetFrom()190 const Node *GetFrom() const 191 { 192 return from_; 193 } GetTo()194 const Node *GetTo() const 195 { 196 return to_; 197 } GetName()198 const CString *GetName() const 199 { 200 ASSERT(GetType() != EdgeType::ELEMENT); 201 return name_; 202 } GetIndex()203 uint32_t GetIndex() const 204 { 205 ASSERT(GetType() == EdgeType::ELEMENT); 206 return index_; 207 } SetName(CString * name)208 void SetName(CString *name) 209 { 210 ASSERT(GetType() != EdgeType::ELEMENT); 211 name_ = name; 212 } UpdateFrom(Node * node)213 void UpdateFrom(Node *node) 214 { 215 from_ = node; 216 } UpdateTo(Node * node)217 void UpdateTo(Node *node) 218 { 219 to_ = node; 220 } 221 static Edge *NewEdge(Chunk &chunk, EdgeType type, Node *from, Node *to, CString *name); 222 static Edge *NewEdge(Chunk &chunk, EdgeType type, Node *from, Node *to, uint32_t index); 223 static constexpr int EDGE_FIELD_COUNT = 3; 224 ~Edge() = default; 225 226 private: 227 EdgeType edgeType_ {EdgeType::DEFAULT}; 228 Node *from_ {nullptr}; 229 Node *to_ {nullptr}; 230 union { 231 CString *name_; 232 uint32_t index_; 233 }; 234 }; 235 236 class TimeStamp { 237 public: TimeStamp(int sequenceId)238 explicit TimeStamp(int sequenceId) : lastSequenceId_(sequenceId), timeStampUs_(TimeStamp::Now()) {} 239 ~TimeStamp() = default; 240 241 DEFAULT_MOVE_SEMANTIC(TimeStamp); 242 DEFAULT_COPY_SEMANTIC(TimeStamp); 243 GetLastSequenceId()244 int GetLastSequenceId() const 245 { 246 return lastSequenceId_; 247 } 248 GetTimeStamp()249 int64_t GetTimeStamp() const 250 { 251 return timeStampUs_; 252 } 253 GetSize()254 uint32_t GetSize() const 255 { 256 return size_; 257 } 258 SetSize(uint32_t size)259 void SetSize(uint32_t size) 260 { 261 size_ = size; 262 } 263 GetCount()264 uint32_t GetCount() const 265 { 266 return count_; 267 } 268 SetCount(uint32_t count)269 void SetCount(uint32_t count) 270 { 271 count_ = count; 272 } 273 274 private: Now()275 static int64_t Now() 276 { 277 struct timeval tv = {0, 0}; 278 gettimeofday(&tv, nullptr); 279 const int THOUSAND = 1000; 280 return tv.tv_usec + tv.tv_sec * THOUSAND * THOUSAND; 281 } 282 283 int lastSequenceId_ {0}; 284 int64_t timeStampUs_ {0}; 285 uint32_t size_ {0}; 286 uint32_t count_ {0}; 287 }; 288 289 class HeapEntryMap { 290 public: 291 HeapEntryMap() = default; 292 ~HeapEntryMap() = default; 293 NO_MOVE_SEMANTIC(HeapEntryMap); 294 NO_COPY_SEMANTIC(HeapEntryMap); 295 Node *FindOrInsertNode(Node *node); 296 Node *FindAndEraseNode(JSTaggedType addr); 297 Node *FindEntry(JSTaggedType addr); 298 void InsertEntry(Node *node); 299 300 private: 301 CUnorderedMap<JSTaggedType, Node *> nodesMap_ {}; 302 }; 303 304 struct FunctionInfo { 305 int functionId = 0; 306 std::string functionName = ""; 307 std::string scriptName = ""; 308 int scriptId = 0; 309 int columnNumber = 0; 310 int lineNumber = 0; 311 }; 312 313 class TraceTree; 314 class TraceNode { 315 public: 316 TraceNode(TraceTree* tree, uint32_t nodeIndex); 317 ~TraceNode(); 318 319 TraceNode(const TraceNode&) = delete; 320 TraceNode& operator=(const TraceNode&) = delete; 321 TraceNode* FindChild(uint32_t nodeIndex); 322 TraceNode* FindOrAddChild(uint32_t nodeIndex); GetNodeIndex()323 uint32_t GetNodeIndex() const 324 { 325 return nodeIndex_; 326 } GetTotalSize()327 uint32_t GetTotalSize() const 328 { 329 return totalSize_; 330 } GetTotalCount()331 uint32_t GetTotalCount() const 332 { 333 return totalCount_; 334 } GetId()335 uint32_t GetId() const 336 { 337 return id_; 338 } GetChildren()339 const std::vector<TraceNode*>& GetChildren() const 340 { 341 return children_; 342 } SetTotalSize(uint32_t totalSize)343 TraceNode &SetTotalSize(uint32_t totalSize) 344 { 345 totalSize_ = totalSize; 346 return *this; 347 } SetTotalCount(uint32_t totalCount)348 TraceNode &SetTotalCount(uint32_t totalCount) 349 { 350 totalCount_ = totalCount; 351 return *this; 352 } 353 354 private: 355 TraceTree* tree_ {nullptr}; 356 uint32_t nodeIndex_ {0}; 357 uint32_t totalSize_ {0}; 358 uint32_t totalCount_ {0}; 359 uint32_t id_ {0}; 360 std::vector<TraceNode*> children_ {}; 361 }; 362 363 class TraceTree { 364 public: TraceTree()365 TraceTree() : nextNodeId_(1), root_(this, 0) 366 { 367 } 368 ~TraceTree() = default; 369 TraceTree(const TraceTree&) = delete; 370 TraceTree& operator=(const TraceTree&) = delete; 371 TraceNode* AddNodeToTree(CVector<uint32_t> traceNodeIndex); GetRoot()372 TraceNode* GetRoot() 373 { 374 return &root_; 375 } GetNextNodeId()376 uint32_t GetNextNodeId() 377 { 378 return nextNodeId_++; 379 } 380 381 private: 382 uint32_t nextNodeId_ {0}; 383 TraceNode root_; 384 }; 385 386 struct Reference { 387 enum class ReferenceType { CONTEXT, ELEMENT, PROPERTY, INTERNAL, HIDDEN, SHORTCUT, WEAK, DEFAULT = PROPERTY }; 388 ReferenceReference389 Reference(const CString &name, JSTaggedValue value) : name_(name), value_(value) {} ReferenceReference390 Reference(const CString &name, JSTaggedValue value, ReferenceType type) : name_(name), value_(value), type_(type) {} ReferenceReference391 Reference(uint32_t index, JSTaggedValue value, ReferenceType type) : index_(index), value_(value), type_(type) {} 392 393 CString name_; 394 uint32_t index_ {-1U}; 395 JSTaggedValue value_; 396 ReferenceType type_ {ReferenceType::DEFAULT}; 397 }; 398 399 class EntryVisitor { 400 public: 401 NO_MOVE_SEMANTIC(EntryVisitor); 402 NO_COPY_SEMANTIC(EntryVisitor); 403 EntryVisitor() = default; 404 ~EntryVisitor() = default; 405 static CString ConvertKey(JSThread *thread, JSTaggedValue key); 406 }; 407 408 class HeapSnapshot { 409 public: 410 static constexpr int SEQ_STEP = 2; 411 NO_MOVE_SEMANTIC(HeapSnapshot); 412 NO_COPY_SEMANTIC(HeapSnapshot); HeapSnapshot(const EcmaVM * vm,StringHashMap * stringTable,const DumpSnapShotOption & dumpOption,const bool trackAllocations,EntryIdMap * entryIdMap)413 HeapSnapshot(const EcmaVM *vm, StringHashMap *stringTable, const DumpSnapShotOption &dumpOption, 414 const bool trackAllocations, EntryIdMap *entryIdMap) 415 : vm_(vm), stringTable_(stringTable), isVmMode_(dumpOption.isVmMode), isPrivate_(dumpOption.isPrivate), 416 captureNumericValue_(dumpOption.captureNumericValue), trackAllocations_(trackAllocations), 417 entryIdMap_(entryIdMap), chunk_(vm->GetNativeAreaAllocator()) {} 418 ~HeapSnapshot(); 419 bool BuildUp(bool isSimplify = false); 420 bool Verify(); 421 422 void PrepareSnapshot(); 423 void UpdateNodes(bool isInFinish = false); 424 Node *AddNode(TaggedObject *address, size_t size); 425 void MoveNode(uintptr_t address, TaggedObject *forwardAddress, size_t size); 426 void RecordSampleTime(); 427 bool FinishSnapshot(); 428 void PushHeapStat(Stream* stream); 429 int AddTraceNode(int sequenceId, int size); 430 bool AddMethodInfo(MethodLiteral *methodLiteral, 431 const JSPandaFile *jsPandaFile, int sequenceId); 432 void AddTraceNodeId(MethodLiteral *methodLiteral); 433 GetTimeStamps()434 const CVector<TimeStamp> &GetTimeStamps() const 435 { 436 return timeStamps_; 437 } 438 GetNodeCount()439 size_t GetNodeCount() const 440 { 441 return nodeCount_; 442 } GetEdgeCount()443 size_t GetEdgeCount() const 444 { 445 return edgeCount_; 446 } GetTotalNodeSize()447 size_t GetTotalNodeSize() const 448 { 449 return totalNodesSize_; 450 } AccumulateNodeSize(size_t size)451 void AccumulateNodeSize(size_t size) 452 { 453 totalNodesSize_ += size; 454 } DecreaseNodeSize(size_t size)455 void DecreaseNodeSize(size_t size) 456 { 457 totalNodesSize_ -= size; 458 } 459 CString *GenerateNodeName(TaggedObject *entry); 460 NodeType GenerateNodeType(TaggedObject *entry); GetNodes()461 const CList<Node *> *GetNodes() const 462 { 463 return &nodes_; 464 } GetEdges()465 const CList<Edge *> *GetEdges() const 466 { 467 return &edges_; 468 } 469 470 CString *GetString(const CString &as); 471 CString *GetArrayString(TaggedArray *array, const CString &as); 472 IsInVmMode()473 bool IsInVmMode() const 474 { 475 return isVmMode_; 476 } 477 IsPrivate()478 bool IsPrivate() const 479 { 480 return isPrivate_; 481 } 482 trackAllocations()483 bool trackAllocations() const 484 { 485 return trackAllocations_; 486 } 487 GetTrackAllocationsStack()488 const CVector<FunctionInfo> &GetTrackAllocationsStack() const 489 { 490 return traceInfoStack_; 491 } 492 GetTraceTree()493 TraceTree* GetTraceTree() 494 { 495 return &traceTree_; 496 } 497 PrepareTraceInfo()498 void PrepareTraceInfo() 499 { 500 struct FunctionInfo info; 501 info.functionName = "(root)"; 502 GetString(info.functionName.c_str()); 503 traceInfoStack_.push_back(info); 504 } GetEcmaStringTable()505 const StringHashMap *GetEcmaStringTable() const 506 { 507 return stringTable_; 508 } 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 NewRootEdge(Node *syntheticRoot, JSTaggedValue value, 546 CUnorderedSet<JSTaggedType> &values, CList<Edge *> &rootEdges); 547 void HandleRoots(Node *syntheticRoot, CUnorderedSet<JSTaggedType> &values, CList<Edge *> &rootEdges); 548 Node *InsertNodeAt(size_t pos, Node *node); 549 Edge *InsertEdgeAt(size_t pos, Edge *edge); 550 551 void LogLeakedLocalHandleBackTrace(ObjectSlot slot); 552 void LogLeakedLocalHandleBackTrace(common::RefField<> &refField); 553 554 CList<Node *> nodes_ {}; 555 CList<Edge *> edges_ {}; 556 CVector<TimeStamp> timeStamps_ {}; 557 uint32_t nodeCount_ {0}; 558 uint32_t edgeCount_ {0}; 559 size_t totalNodesSize_ {0}; 560 HeapEntryMap entryMap_; 561 panda::ecmascript::HeapRootVisitor rootVisitor_; 562 const EcmaVM *vm_; 563 StringHashMap *stringTable_ {nullptr}; 564 bool isVmMode_ {true}; 565 bool isPrivate_ {false}; 566 bool captureNumericValue_ {false}; 567 Node* privateStringNode_ {nullptr}; 568 bool trackAllocations_ {false}; 569 CVector<FunctionInfo> traceInfoStack_ {}; 570 CMap<MethodLiteral *, struct FunctionInfo> stackInfo_; 571 CMap<std::string, int> scriptIdMap_; 572 TraceTree traceTree_; 573 CMap<MethodLiteral *, uint32_t> methodToTraceNodeId_; 574 CVector<uint32_t> traceNodeIndex_; 575 EntryIdMap* entryIdMap_; 576 Chunk chunk_; 577 friend class HeapSnapShotFriendTest; 578 }; 579 580 } // namespace panda::ecmascript 581 #endif // ECMASCRIPT_DFX_HPROF_HEAP_SNAPSHOT_H 582