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