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