1/* 2 * Copyright (c) 2021-2022 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#include "ecmascript/dfx/hprof/heap_snapshot.h" 17 18#include <functional> 19 20#include "ecmascript/ecma_string-inl.h" 21#include "ecmascript/jspandafile/program_object.h" 22 23namespace panda::ecmascript { 24CString *HeapSnapshot::GetString(const CString &as) 25{ 26 return stringTable_->GetString(as); 27} 28 29CString *HeapSnapshot::GetArrayString(TaggedArray *array, const CString &as) 30{ 31 CString arrayName = as; 32 arrayName.append(ToCString(array->GetLength())); 33 arrayName.append("]"); 34 return GetString(arrayName); // String type was handled singly, see#GenerateStringNode 35} 36 37Node *Node::NewNode(Chunk *chunk, NodeId id, size_t index, const CString *name, NodeType type, size_t size, 38 size_t nativeSize, JSTaggedType entry, bool isLive) 39{ 40 auto node = chunk->New<Node>(id, index, name, type, size, nativeSize, 0, entry, isLive); 41 if (UNLIKELY(node == nullptr)) { 42 LOG_FULL(FATAL) << "internal allocator failed"; 43 UNREACHABLE(); 44 } 45 return node; 46} 47 48Edge *Edge::NewEdge(Chunk *chunk, EdgeType type, Node *from, Node *to, CString *name) 49{ 50 auto edge = chunk->New<Edge>(type, from, to, name); 51 if (UNLIKELY(edge == nullptr)) { 52 LOG_FULL(FATAL) << "internal allocator failed"; 53 UNREACHABLE(); 54 } 55 return edge; 56} 57 58Edge *Edge::NewEdge(Chunk *chunk, EdgeType type, Node *from, Node *to, uint32_t index) 59{ 60 auto edge = chunk->New<Edge>(type, from, to, index); 61 if (UNLIKELY(edge == nullptr)) { 62 LOG_FULL(FATAL) << "internal allocator failed"; 63 UNREACHABLE(); 64 } 65 return edge; 66} 67 68HeapSnapshot::~HeapSnapshot() 69{ 70 for (Node *node : nodes_) { 71 chunk_->Delete(node); 72 } 73 for (Edge *edge : edges_) { 74 chunk_->Delete(edge); 75 } 76 nodes_.clear(); 77 edges_.clear(); 78 traceInfoStack_.clear(); 79 stackInfo_.clear(); 80 scriptIdMap_.clear(); 81 methodToTraceNodeId_.clear(); 82 traceNodeIndex_.clear(); 83 entryIdMap_ = nullptr; 84 chunk_ = nullptr; 85 stringTable_ = nullptr; 86} 87 88bool HeapSnapshot::BuildUp(bool isSimplify) 89{ 90 FillNodes(true, isSimplify); 91 FillEdges(isSimplify); 92 AddSyntheticRoot(); 93 return Verify(); 94} 95 96bool HeapSnapshot::Verify() 97{ 98 GetString(CString("HeapVerify:").append(ToCString(totalNodesSize_))); 99 return (edgeCount_ > nodeCount_) && (totalNodesSize_ > 0); 100} 101 102void HeapSnapshot::PrepareSnapshot() 103{ 104 FillNodes(); 105 if (trackAllocations()) { 106 PrepareTraceInfo(); 107 } 108} 109 110void HeapSnapshot::UpdateNodes(bool isInFinish) 111{ 112 for (Node *node : nodes_) { 113 node->SetLive(false); 114 } 115 FillNodes(isInFinish); 116 for (auto iter = nodes_.begin(); iter != nodes_.end();) { 117 if (!(*iter)->IsLive()) { 118 entryMap_.FindAndEraseNode((*iter)->GetAddress()); 119 entryIdMap_->EraseId((*iter)->GetAddress()); 120 DecreaseNodeSize((*iter)->GetSelfSize()); 121 chunk_->Delete(*iter); 122 iter = nodes_.erase(iter); 123 nodeCount_--; 124 } else { 125 iter++; 126 } 127 } 128} 129 130bool HeapSnapshot::FinishSnapshot() 131{ 132 UpdateNodes(true); 133 FillEdges(); 134 AddSyntheticRoot(); 135 return Verify(); 136} 137 138void HeapSnapshot::RecordSampleTime() 139{ 140 timeStamps_.emplace_back(entryIdMap_->GetLastId()); 141} 142 143void HeapSnapshot::PushHeapStat(Stream* stream) 144{ 145 CVector<HeapStat> statsBuffer; 146 if (stream == nullptr) { 147 LOG_DEBUGGER(ERROR) << "HeapSnapshot::PushHeapStat::stream is nullptr"; 148 return; 149 } 150 int32_t preChunkSize = stream->GetSize(); 151 int32_t sequenceId = 0; 152 int64_t timeStampUs = 0; 153 auto iter = nodes_.begin(); 154 for (size_t timeIndex = 0; timeIndex < timeStamps_.size(); ++timeIndex) { 155 TimeStamp& timeStamp = timeStamps_[timeIndex]; 156 sequenceId = timeStamp.GetLastSequenceId(); 157 timeStampUs = timeStamp.GetTimeStamp(); 158 uint32_t nodesSize = 0; 159 uint32_t nodesCount = 0; 160 while (iter != nodes_.end() && (*iter)->GetId() <= static_cast<uint32_t>(sequenceId)) { 161 nodesCount++; 162 nodesSize += (*iter)->GetSelfSize(); 163 iter++; 164 } 165 if ((timeStamp.GetCount() != nodesCount) || (timeStamp.GetSize() != nodesSize)) { 166 timeStamp.SetCount(nodesCount); 167 timeStamp.SetSize(nodesSize); 168 statsBuffer.emplace_back(static_cast<int32_t>(timeIndex), nodesCount, nodesSize); 169 if (static_cast<int32_t>(statsBuffer.size()) >= preChunkSize) { 170 stream->UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size())); 171 statsBuffer.clear(); 172 } 173 } 174 } 175 if (!statsBuffer.empty()) { 176 stream->UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size())); 177 statsBuffer.clear(); 178 } 179 stream->UpdateLastSeenObjectId(sequenceId, timeStampUs); 180} 181 182Node *HeapSnapshot::AddNode(TaggedObject *address, size_t size) 183{ 184 return GenerateNode(JSTaggedValue(address), size); 185} 186 187void HeapSnapshot::MoveNode(uintptr_t address, TaggedObject *forwardAddress, size_t size) 188{ 189 if (address == reinterpret_cast<uintptr_t>(forwardAddress)) { 190 return; 191 } 192 193 Node *node = entryMap_.FindAndEraseNode(static_cast<JSTaggedType>(address)); 194 if (node != nullptr) { 195 ASSERT(node->GetId() <= UINT_MAX); 196 197 Node *oldNode = entryMap_.FindAndEraseNode(Node::NewAddress(forwardAddress)); 198 if (oldNode != nullptr) { 199 oldNode->SetAddress(Node::NewAddress(TaggedObject::Cast(nullptr))); 200 } 201 202 // Size and name may change during its life for some types(such as string, array and etc). 203 if (forwardAddress->GetClass() != nullptr) { 204 node->SetName(GenerateNodeName(forwardAddress)); 205 } 206 if (JSTaggedValue(forwardAddress).IsString()) { 207 node->SetSelfSize(EcmaStringAccessor(forwardAddress).GetFlatStringSize()); 208 } else { 209 node->SetSelfSize(size); 210 } 211 node->SetAddress(Node::NewAddress(forwardAddress)); 212 entryMap_.InsertEntry(node); 213 } else { 214 LOG_DEBUGGER(WARN) << "Untracked object moves from " << address << " to " << forwardAddress; 215 GenerateNode(JSTaggedValue(forwardAddress), size, false); 216 } 217} 218 219// NOLINTNEXTLINE(readability-function-size) 220CString *HeapSnapshot::GenerateNodeName(TaggedObject *entry) 221{ 222 auto *hCls = entry->GetClass(); 223 JSType type = hCls->GetObjectType(); 224 switch (type) { 225 case JSType::TAGGED_ARRAY: 226 return GetArrayString(TaggedArray::Cast(entry), "ArkInternalArray["); 227 case JSType::LEXICAL_ENV: 228 return GetArrayString(TaggedArray::Cast(entry), "LexicalEnv["); 229 case JSType::SENDABLE_ENV: 230 return GetArrayString(TaggedArray::Cast(entry), "SendableEnv["); 231 case JSType::CONSTANT_POOL: 232 return GetArrayString(TaggedArray::Cast(entry), "ArkInternalConstantPool["); 233 case JSType::PROFILE_TYPE_INFO: 234 return GetArrayString(TaggedArray::Cast(entry), "ArkInternalProfileTypeInfo["); 235 case JSType::TAGGED_DICTIONARY: 236 return GetArrayString(TaggedArray::Cast(entry), "ArkInternalDict["); 237 case JSType::AOT_LITERAL_INFO: 238 return GetArrayString(TaggedArray::Cast(entry), "ArkInternalAOTLiteralInfo["); 239 case JSType::VTABLE: 240 return GetArrayString(TaggedArray::Cast(entry), "ArkInternalVTable["); 241 case JSType::COW_TAGGED_ARRAY: 242 return GetArrayString(TaggedArray::Cast(entry), "ArkInternalCOWArray["); 243 case JSType::HCLASS: 244 return GetString("HiddenClass(NonMovable)"); 245 case JSType::LINKED_NODE: 246 return GetString("LinkedNode"); 247 case JSType::TRACK_INFO: 248 return GetString("TrackInfo"); 249 case JSType::LINE_STRING: 250 case JSType::CONSTANT_STRING: 251 case JSType::TREE_STRING: 252 case JSType::SLICED_STRING: 253 return GetString("BaseString"); 254 case JSType::JS_OBJECT: { 255 CString objName = CString("JSObject"); // Ctor-name 256 return GetString(objName); 257 } 258 case JSType::JS_SHARED_OBJECT: { 259 return GetString("JSSharedObject"); 260 } 261 case JSType::JS_SHARED_FUNCTION: { 262 return GetString("JSSharedFunction"); 263 } 264 case JSType::FREE_OBJECT_WITH_ONE_FIELD: 265 case JSType::FREE_OBJECT_WITH_NONE_FIELD: 266 case JSType::FREE_OBJECT_WITH_TWO_FIELD: 267 return GetString("FreeObject"); 268 case JSType::JS_NATIVE_POINTER: 269 return GetString("JSNativePointer"); 270 case JSType::JS_FUNCTION_BASE: 271 return GetString("JSFunctionBase"); 272 case JSType::JS_FUNCTION: 273 return GetString(CString("JSFunction")); 274 case JSType::FUNCTION_TEMPLATE: 275 return GetString(CString("ArkInternalFunctionTemplate")); 276 case JSType::JS_ERROR: 277 return GetString("Error"); 278 case JSType::JS_EVAL_ERROR: 279 return GetString("Eval Error"); 280 case JSType::JS_RANGE_ERROR: 281 return GetString("Range Error"); 282 case JSType::JS_TYPE_ERROR: 283 return GetString("Type Error"); 284 case JSType::JS_AGGREGATE_ERROR: 285 return GetString("Aggregate Error"); 286 case JSType::JS_REFERENCE_ERROR: 287 return GetString("Reference Error"); 288 case JSType::JS_URI_ERROR: 289 return GetString("Uri Error"); 290 case JSType::JS_SYNTAX_ERROR: 291 return GetString("Syntax Error"); 292 case JSType::JS_OOM_ERROR: 293 return GetString("OutOfMemory Error"); 294 case JSType::JS_TERMINATION_ERROR: 295 return GetString("Termination Error"); 296 case JSType::JS_REG_EXP: 297 return GetString("Regexp"); 298 case JSType::JS_SET: 299 return GetString("Set"); 300 case JSType::JS_SHARED_SET: 301 return GetString("SharedSet"); 302 case JSType::JS_MAP: 303 return GetString("Map"); 304 case JSType::JS_SHARED_MAP: 305 return GetString("SharedMap"); 306 case JSType::JS_WEAK_SET: 307 return GetString("WeakSet"); 308 case JSType::JS_WEAK_MAP: 309 return GetString("WeakMap"); 310 case JSType::JS_DATE: 311 return GetString("Date"); 312 case JSType::JS_BOUND_FUNCTION: 313 return GetString("Bound Function"); 314 case JSType::JS_ARRAY: 315 return GetString("JSArray"); 316 case JSType::JS_TYPED_ARRAY: 317 return GetString("Typed Array"); 318 case JSType::JS_INT8_ARRAY: 319 return GetString("Int8 Array"); 320 case JSType::JS_UINT8_ARRAY: 321 return GetString("Uint8 Array"); 322 case JSType::JS_UINT8_CLAMPED_ARRAY: 323 return GetString("Uint8 Clamped Array"); 324 case JSType::JS_INT16_ARRAY: 325 return GetString("Int16 Array"); 326 case JSType::JS_UINT16_ARRAY: 327 return GetString("Uint16 Array"); 328 case JSType::JS_INT32_ARRAY: 329 return GetString("Int32 Array"); 330 case JSType::JS_UINT32_ARRAY: 331 return GetString("Uint32 Array"); 332 case JSType::JS_FLOAT32_ARRAY: 333 return GetString("Float32 Array"); 334 case JSType::JS_FLOAT64_ARRAY: 335 return GetString("Float64 Array"); 336 case JSType::JS_BIGINT64_ARRAY: 337 return GetString("BigInt64 Array"); 338 case JSType::JS_BIGUINT64_ARRAY: 339 return GetString("BigUint64 Array"); 340 case JSType::JS_ARGUMENTS: 341 return GetString("Arguments"); 342 case JSType::BIGINT: 343 return GetString("BigInt"); 344 case JSType::JS_PROXY: 345 return GetString("Proxy"); 346 case JSType::JS_PRIMITIVE_REF: 347 return GetString("Primitive"); 348 case JSType::JS_DATA_VIEW: 349 return GetString("DataView"); 350 case JSType::JS_ITERATOR: 351 return GetString("Iterator"); 352 case JSType::JS_FORIN_ITERATOR: 353 return GetString("ForinInterator"); 354 case JSType::JS_MAP_ITERATOR: 355 return GetString("MapIterator"); 356 case JSType::JS_SHARED_MAP_ITERATOR: 357 return GetString("SharedMapIterator"); 358 case JSType::JS_SET_ITERATOR: 359 return GetString("SetIterator"); 360 case JSType::JS_SHARED_SET_ITERATOR: 361 return GetString("SharedSetIterator"); 362 case JSType::JS_REG_EXP_ITERATOR: 363 return GetString("RegExpIterator"); 364 case JSType::JS_ARRAY_ITERATOR: 365 return GetString("ArrayIterator"); 366 case JSType::JS_STRING_ITERATOR: 367 return GetString("StringIterator"); 368 case JSType::JS_ARRAY_BUFFER: 369 return GetString("ArrayBuffer"); 370 case JSType::JS_SENDABLE_ARRAY_BUFFER: 371 return GetString("SendableArrayBuffer"); 372 case JSType::JS_SHARED_ARRAY: 373 return GetString("SharedArray"); 374 case JSType::JS_SHARED_ARRAY_BUFFER: 375 return GetString("SharedArrayBuffer"); 376 case JSType::JS_PROXY_REVOC_FUNCTION: 377 return GetString("ProxyRevocFunction"); 378 case JSType::PROMISE_REACTIONS: 379 return GetString("PromiseReaction"); 380 case JSType::PROMISE_CAPABILITY: 381 return GetString("PromiseCapability"); 382 case JSType::ASYNC_GENERATOR_REQUEST: 383 return GetString("AsyncGeneratorRequest"); 384 case JSType::PROMISE_ITERATOR_RECORD: 385 return GetString("PromiseIteratorRecord"); 386 case JSType::PROMISE_RECORD: 387 return GetString("PromiseRecord"); 388 case JSType::RESOLVING_FUNCTIONS_RECORD: 389 return GetString("ResolvingFunctionsRecord"); 390 case JSType::JS_PROMISE: 391 return GetString("Promise"); 392 case JSType::JS_PROMISE_REACTIONS_FUNCTION: 393 return GetString("PromiseReactionsFunction"); 394 case JSType::JS_PROMISE_EXECUTOR_FUNCTION: 395 return GetString("PromiseExecutorFunction"); 396 case JSType::JS_ASYNC_MODULE_FULFILLED_FUNCTION: 397 return GetString("AsyncModuleFulfilledFunction"); 398 case JSType::JS_ASYNC_MODULE_REJECTED_FUNCTION: 399 return GetString("AsyncModuleRejectedFunction"); 400 case JSType::JS_ASYNC_FROM_SYNC_ITER_UNWARP_FUNCTION: 401 return GetString("AsyncFromSyncIterUnwarpFunction"); 402 case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION: 403 return GetString("PromiseAllResolveElementFunction"); 404 case JSType::JS_PROMISE_ANY_REJECT_ELEMENT_FUNCTION: 405 return GetString("PromiseAnyRejectElementFunction"); 406 case JSType::JS_PROMISE_ALL_SETTLED_ELEMENT_FUNCTION: 407 return GetString("PromiseAllSettledElementFunction"); 408 case JSType::JS_PROMISE_FINALLY_FUNCTION: 409 return GetString("PromiseFinallyFunction"); 410 case JSType::JS_PROMISE_VALUE_THUNK_OR_THROWER_FUNCTION: 411 return GetString("PromiseValueThunkOrThrowerFunction"); 412 case JSType::JS_ASYNC_GENERATOR_RESUME_NEXT_RETURN_PROCESSOR_RST_FTN: 413 return GetString("AsyncGeneratorResumeNextReturnProcessorRstFtn"); 414 case JSType::JS_GENERATOR_FUNCTION: 415 return GetString("JSGeneratorFunction"); 416 case JSType::JS_ASYNC_GENERATOR_FUNCTION: 417 return GetString("JSAsyncGeneratorFunction"); 418 case JSType::SYMBOL: 419 return GetString("Symbol"); 420 case JSType::JS_ASYNC_FUNCTION: 421 return GetString("AsyncFunction"); 422 case JSType::JS_SHARED_ASYNC_FUNCTION: 423 return GetString("SharedAsyncFunction"); 424 case JSType::JS_INTL_BOUND_FUNCTION: 425 return GetString("JSIntlBoundFunction"); 426 case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION: 427 return GetString("AsyncAwaitStatusFunction"); 428 case JSType::JS_ASYNC_FUNC_OBJECT: 429 return GetString("AsyncFunctionObject"); 430 case JSType::JS_REALM: 431 return GetString("Realm"); 432 case JSType::JS_GLOBAL_OBJECT: 433 return GetString("GlobalObject"); 434 case JSType::JS_INTL: 435 return GetString("JSIntl"); 436 case JSType::JS_LOCALE: 437 return GetString("JSLocale"); 438 case JSType::JS_DATE_TIME_FORMAT: 439 return GetString("JSDateTimeFormat"); 440 case JSType::JS_RELATIVE_TIME_FORMAT: 441 return GetString("JSRelativeTimeFormat"); 442 case JSType::JS_NUMBER_FORMAT: 443 return GetString("JSNumberFormat"); 444 case JSType::JS_COLLATOR: 445 return GetString("JSCollator"); 446 case JSType::JS_PLURAL_RULES: 447 return GetString("JSPluralRules"); 448 case JSType::JS_DISPLAYNAMES: 449 return GetString("JSDisplayNames"); 450 case JSType::JS_SEGMENTER: 451 return GetString("JSSegmenter"); 452 case JSType::JS_SEGMENTS: 453 return GetString("JSSegments"); 454 case JSType::JS_SEGMENT_ITERATOR: 455 return GetString("JSSegmentIterator"); 456 case JSType::JS_LIST_FORMAT: 457 return GetString("JSListFormat"); 458 case JSType::JS_GENERATOR_OBJECT: 459 return GetString("JSGeneratorObject"); 460 case JSType::JS_ASYNC_GENERATOR_OBJECT: 461 return GetString("JSAsyncGeneratorObject"); 462 case JSType::JS_GENERATOR_CONTEXT: 463 return GetString("JSGeneratorContext"); 464 case JSType::ACCESSOR_DATA: 465 return GetString("AccessorData"); 466 case JSType::INTERNAL_ACCESSOR: 467 return GetString("InternalAccessor"); 468 case JSType::MICRO_JOB_QUEUE: 469 return GetString("MicroJobQueue"); 470 case JSType::PENDING_JOB: 471 return GetString("PendingJob"); 472 case JSType::COMPLETION_RECORD: 473 return GetString("CompletionRecord"); 474 case JSType::JS_API_ARRAY_LIST: 475 return GetString("ArrayList"); 476 case JSType::JS_API_ARRAYLIST_ITERATOR: 477 return GetString("ArrayListIterator"); 478 case JSType::JS_API_HASH_MAP: 479 return GetString("HashMap"); 480 case JSType::JS_API_HASH_SET: 481 return GetString("HashSet"); 482 case JSType::JS_API_HASHMAP_ITERATOR: 483 return GetString("HashMapIterator"); 484 case JSType::JS_API_HASHSET_ITERATOR: 485 return GetString("HashSetIterator"); 486 case JSType::JS_API_LIGHT_WEIGHT_MAP: 487 return GetString("LightWeightMap"); 488 case JSType::JS_API_LIGHT_WEIGHT_MAP_ITERATOR: 489 return GetString("LightWeightMapIterator"); 490 case JSType::JS_API_LIGHT_WEIGHT_SET: 491 return GetString("LightWeightSet"); 492 case JSType::JS_API_LIGHT_WEIGHT_SET_ITERATOR: 493 return GetString("LightWeightSetIterator"); 494 case JSType::JS_API_TREE_MAP: 495 return GetString("TreeMap"); 496 case JSType::JS_API_TREE_SET: 497 return GetString("TreeSet"); 498 case JSType::JS_API_TREEMAP_ITERATOR: 499 return GetString("TreeMapIterator"); 500 case JSType::JS_API_TREESET_ITERATOR: 501 return GetString("TreeSetIterator"); 502 case JSType::JS_API_VECTOR: 503 return GetString("Vector"); 504 case JSType::JS_API_VECTOR_ITERATOR: 505 return GetString("VectorIterator"); 506 case JSType::JS_API_BITVECTOR: 507 return GetString("BitVector"); 508 case JSType::JS_API_BITVECTOR_ITERATOR: 509 return GetString("BitVectorIterator"); 510 case JSType::JS_API_QUEUE: 511 return GetString("Queue"); 512 case JSType::JS_API_QUEUE_ITERATOR: 513 return GetString("QueueIterator"); 514 case JSType::JS_API_DEQUE: 515 return GetString("Deque"); 516 case JSType::JS_API_DEQUE_ITERATOR: 517 return GetString("DequeIterator"); 518 case JSType::JS_API_STACK: 519 return GetString("Stack"); 520 case JSType::JS_API_STACK_ITERATOR: 521 return GetString("StackIterator"); 522 case JSType::JS_API_LIST: 523 return GetString("List"); 524 case JSType::JS_API_LINKED_LIST: 525 return GetString("LinkedList"); 526 case JSType::SOURCE_TEXT_MODULE_RECORD: 527 return GetString("SourceTextModule"); 528 case JSType::IMPORTENTRY_RECORD: 529 return GetString("ImportEntry"); 530 case JSType::LOCAL_EXPORTENTRY_RECORD: 531 return GetString("LocalExportEntry"); 532 case JSType::INDIRECT_EXPORTENTRY_RECORD: 533 return GetString("IndirectExportEntry"); 534 case JSType::STAR_EXPORTENTRY_RECORD: 535 return GetString("StarExportEntry"); 536 case JSType::RESOLVEDBINDING_RECORD: 537 return GetString("ResolvedBinding"); 538 case JSType::RESOLVEDINDEXBINDING_RECORD: 539 return GetString("ResolvedIndexBinding"); 540 case JSType::RESOLVEDRECORDINDEXBINDING_RECORD: 541 return GetString("ResolvedRecordIndexBinding"); 542 case JSType::RESOLVEDRECORDBINDING_RECORD: 543 return GetString("ResolvedRecordBinding"); 544 case JSType::JS_MODULE_NAMESPACE: 545 return GetString("ModuleNamespace"); 546 case JSType::JS_API_PLAIN_ARRAY: 547 return GetString("PlainArray"); 548 case JSType::JS_API_PLAIN_ARRAY_ITERATOR: 549 return GetString("PlainArrayIterator"); 550 case JSType::JS_CJS_EXPORTS: 551 return GetString("CJS Exports"); 552 case JSType::JS_CJS_MODULE: 553 return GetString("CJS Module"); 554 case JSType::JS_CJS_REQUIRE: 555 return GetString("CJS Require"); 556 case JSType::METHOD: 557 return GetString("Method"); 558 default: 559 break; 560 } 561 if (IsInVmMode()) { 562 switch (type) { 563 case JSType::PROPERTY_BOX: 564 return GetString("PropertyBox"); 565 case JSType::GLOBAL_ENV: 566 return GetString("GlobalEnv"); 567 case JSType::PROTOTYPE_HANDLER: 568 return GetString("ProtoTypeHandler"); 569 case JSType::TRANSITION_HANDLER: 570 return GetString("TransitionHandler"); 571 case JSType::TRANS_WITH_PROTO_HANDLER: 572 return GetString("TransWithProtoHandler"); 573 case JSType::STORE_TS_HANDLER: 574 return GetString("StoreTSHandler"); 575 case JSType::PROTO_CHANGE_MARKER: 576 return GetString("ProtoChangeMarker"); 577 case JSType::MARKER_CELL: 578 return GetString("MarkerCell"); 579 case JSType::PROTOTYPE_INFO: 580 return GetString("ProtoChangeDetails"); 581 case JSType::TEMPLATE_MAP: 582 return GetString("TemplateMap"); 583 case JSType::PROGRAM: 584 return GetString("Program"); 585 case JSType::MACHINE_CODE_OBJECT: 586 return GetString("MachineCode"); 587 case JSType::CLASS_INFO_EXTRACTOR: 588 return GetString("ClassInfoExtractor"); 589 default: 590 break; 591 } 592 } else { 593 return GetString("Hidden Object"); 594 } 595 return GetString(CString("UnKnownType").append(std::to_string(static_cast<int>(type)))); 596} 597 598NodeType HeapSnapshot::GenerateNodeType(TaggedObject *entry) 599{ 600 NodeType nodeType = NodeType::DEFAULT; 601 auto *hCls = entry->GetClass(); 602 JSType type = hCls->GetObjectType(); 603 604 if (hCls->IsTaggedArray()) { 605 nodeType = NodeType::ARRAY; 606 } else if (hCls->IsHClass()) { 607 nodeType = NodeType::DEFAULT; 608 } else if (type == JSType::PROPERTY_BOX) { 609 nodeType = NodeType::HIDDEN; 610 } else if (type == JSType::JS_ARRAY || type == JSType::JS_TYPED_ARRAY) { 611 nodeType = NodeType::OBJECT; 612 } else if (type == JSType::JS_OBJECT || type == JSType::JS_SHARED_OBJECT) { 613 nodeType = NodeType::OBJECT; 614 } else if (type >= JSType::JS_FUNCTION_FIRST && type <= JSType::JS_FUNCTION_LAST) { 615 nodeType = NodeType::CLOSURE; 616 } else if (type == JSType::JS_BOUND_FUNCTION) { 617 nodeType = NodeType::DEFAULT; 618 } else if (type == JSType::JS_FUNCTION_BASE) { 619 nodeType = NodeType::DEFAULT; 620 } else if (type == JSType::JS_REG_EXP) { 621 nodeType = NodeType::REGEXP; 622 } else if (type == JSType::SYMBOL) { 623 nodeType = NodeType::SYMBOL; 624 } else if (type == JSType::JS_PRIMITIVE_REF) { 625 nodeType = NodeType::HEAPNUMBER; 626 } else if (type == JSType::BIGINT) { 627 nodeType = NodeType::BIGINT; 628 } else { 629 nodeType = NodeType::DEFAULT; 630 } 631 632 return nodeType; 633} 634 635void HeapSnapshot::FillNodes(bool isInFinish, bool isSimplify) 636{ 637 // Iterate Heap Object 638 auto heap = vm_->GetHeap(); 639 if (heap != nullptr) { 640 heap->IterateOverObjects([this, isInFinish, isSimplify](TaggedObject *obj) { 641 GenerateNode(JSTaggedValue(obj), 0, isInFinish, isSimplify); 642 }, isSimplify); 643 } 644} 645 646Node *HeapSnapshot::HandleStringNode(JSTaggedValue &entry, size_t &size, bool &isInFinish, bool isBinMod) 647{ 648 Node* node = nullptr; 649 if (isPrivate_) { 650 node = GeneratePrivateStringNode(size); 651 } else { 652 node = GenerateStringNode(entry, size, isInFinish, isBinMod); 653 } 654 if (node == nullptr) { 655 LOG_ECMA(ERROR) << "string node nullptr"; 656 } 657 return node; 658} 659 660Node *HeapSnapshot::HandleFunctionNode(JSTaggedValue &entry, size_t &size, bool &isInFinish) 661{ 662 Node* node = GenerateFunctionNode(entry, size, isInFinish); 663 if (node == nullptr) { 664 LOG_ECMA(ERROR) << "function node nullptr"; 665 } 666 return node; 667} 668 669Node *HeapSnapshot::HandleObjectNode(JSTaggedValue &entry, size_t &size, bool &isInFinish) 670{ 671 Node* node = GenerateObjectNode(entry, size, isInFinish); 672 if (node == nullptr) { 673 LOG_ECMA(ERROR) << "object node nullptr"; 674 } 675 return node; 676} 677 678Node *HeapSnapshot::HandleBaseClassNode(size_t size, bool idExist, NodeId &sequenceId, 679 TaggedObject* obj, JSTaggedType &addr) 680{ 681 size_t selfSize = (size != 0) ? size : obj->GetClass()->SizeFromJSHClass(obj); 682 size_t nativeSize = 0; 683 if (obj->GetClass()->IsJSNativePointer()) { 684 nativeSize = JSNativePointer::Cast(obj)->GetBindingSize(); 685 } 686 Node* node = Node::NewNode(chunk_, sequenceId, nodeCount_, GenerateNodeName(obj), GenerateNodeType(obj), 687 selfSize, nativeSize, addr); 688 entryMap_.InsertEntry(node); 689 if (!idExist) { 690 entryIdMap_->InsertId(addr, sequenceId); 691 } 692 InsertNodeUnique(node); 693 ASSERT(entryMap_.FindEntry(node->GetAddress())->GetAddress() == node->GetAddress()); 694 return node; 695} 696 697CString HeapSnapshot::GeneratePrimitiveNameString(JSTaggedValue &entry) 698{ 699 CString primitiveName; 700 if (entry.IsInt()) { 701 primitiveName.append("Int:"); 702 if (!isPrivate_) { 703 primitiveName.append(ToCString(entry.GetInt())); 704 } 705 } else if (entry.IsDouble()) { 706 primitiveName.append("Double:"); 707 if (!isPrivate_) { 708 primitiveName.append(FloatToCString(entry.GetDouble())); 709 } 710 } else if (entry.IsHole()) { 711 primitiveName.append("Hole"); 712 } else if (entry.IsNull()) { 713 primitiveName.append("Null"); 714 } else if (entry.IsTrue()) { 715 primitiveName.append("Boolean:true"); 716 } else if (entry.IsFalse()) { 717 primitiveName.append("Boolean:false"); 718 } else if (entry.IsException()) { 719 primitiveName.append("Exception"); 720 } else if (entry.IsUndefined()) { 721 primitiveName.append("Undefined"); 722 } else { 723 primitiveName.append("Illegal_Primitive"); 724 } 725 return primitiveName; 726} 727 728Node *HeapSnapshot::GenerateNode(JSTaggedValue entry, size_t size, bool isInFinish, bool isSimplify, bool isBinMod) 729{ 730 Node *node = nullptr; 731 if (entry.IsHeapObject()) { 732 if (entry.IsWeak()) { 733 entry.RemoveWeakTag(); 734 } 735 if (entry.IsString()) { 736 return HandleStringNode(entry, size, isInFinish, isBinMod); 737 } 738 if (entry.IsJSFunction()) { 739 return HandleFunctionNode(entry, size, isInFinish); 740 } 741 if (entry.IsOnlyJSObject()) { 742 return HandleObjectNode(entry, size, isInFinish); 743 } 744 TaggedObject *obj = entry.GetTaggedObject(); 745 auto *baseClass = obj->GetClass(); 746 if (baseClass != nullptr) { 747 JSTaggedType addr = entry.GetRawData(); 748 Node *existNode = entryMap_.FindEntry(addr); // Fast Index 749 auto [idExist, sequenceId] = entryIdMap_->FindId(addr); 750 if (existNode == nullptr) { 751 return HandleBaseClassNode(size, idExist, sequenceId, obj, addr); 752 } else { 753 existNode->SetLive(true); 754 return existNode; 755 } 756 } 757 } else if (!isSimplify) { 758 if ((entry.IsInt() || entry.IsDouble()) && !captureNumericValue_) { 759 return nullptr; 760 } 761 CString primitiveName = GeneratePrimitiveNameString(entry); 762 // A primitive value with tag will be regarded as a pointer 763 JSTaggedType addr = entry.GetRawData(); 764 Node *existNode = entryMap_.FindEntry(addr); 765 if (existNode != nullptr) { 766 existNode->SetLive(true); 767 return existNode; 768 } 769 auto [idExist, sequenceId] = entryIdMap_->FindId(addr); 770 node = Node::NewNode(chunk_, sequenceId, nodeCount_, GetString(primitiveName), NodeType::HEAPNUMBER, 0, 771 0, addr); 772 entryMap_.InsertEntry(node); // Fast Index 773 if (!idExist) { 774 entryIdMap_->InsertId(addr, sequenceId); 775 } 776 InsertNodeUnique(node); 777 } 778 return node; 779} 780 781TraceNode::TraceNode(TraceTree* tree, uint32_t nodeIndex) 782 : tree_(tree), 783 nodeIndex_(nodeIndex), 784 totalSize_(0), 785 totalCount_(0), 786 id_(tree->GetNextNodeId()) 787{ 788} 789 790TraceNode::~TraceNode() 791{ 792 for (TraceNode* node : children_) { 793 delete node; 794 } 795 children_.clear(); 796} 797 798TraceNode* TraceTree::AddNodeToTree(CVector<uint32_t> traceNodeIndex) 799{ 800 uint32_t len = traceNodeIndex.size(); 801 if (len == 0) { 802 return nullptr; 803 } 804 805 TraceNode* node = GetRoot(); 806 for (int i = static_cast<int>(len) - 1; i >= 0; i--) { 807 node = node->FindOrAddChild(traceNodeIndex[i]); 808 } 809 return node; 810} 811 812TraceNode* TraceNode::FindOrAddChild(uint32_t nodeIndex) 813{ 814 TraceNode* child = FindChild(nodeIndex); 815 if (child == nullptr) { 816 child = new TraceNode(tree_, nodeIndex); 817 children_.push_back(child); 818 } 819 return child; 820} 821 822TraceNode* TraceNode::FindChild(uint32_t nodeIndex) 823{ 824 for (TraceNode* node : children_) { 825 if (node->GetNodeIndex() == nodeIndex) { 826 return node; 827 } 828 } 829 return nullptr; 830} 831 832void HeapSnapshot::AddTraceNodeId(MethodLiteral *methodLiteral) 833{ 834 uint32_t traceNodeId = 0; 835 auto result = methodToTraceNodeId_.find(methodLiteral); 836 if (result == methodToTraceNodeId_.end()) { 837 ASSERT(traceInfoStack_.size() > 0); 838 traceNodeId = traceInfoStack_.size() - 1; 839 methodToTraceNodeId_.emplace(methodLiteral, traceNodeId); 840 } else { 841 traceNodeId = result->second; 842 } 843 traceNodeIndex_.emplace_back(traceNodeId); 844} 845 846int HeapSnapshot::AddTraceNode(int sequenceId, int size) 847{ 848 traceNodeIndex_.clear(); 849 auto thread = vm_->GetJSThread(); 850 JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetCurrentFrame()); 851 FrameIterator it(current, thread); 852 for (; !it.Done(); it.Advance<GCVisitedFlag::VISITED>()) { 853 if (!it.IsJSFrame()) { 854 continue; 855 } 856 auto method = it.CheckAndGetMethod(); 857 if (method == nullptr || method->IsNativeWithCallField()) { 858 continue; 859 } 860 MethodLiteral *methodLiteral = method->GetMethodLiteral(); 861 if (methodLiteral == nullptr) { 862 continue; 863 } 864 if (stackInfo_.count(methodLiteral) == 0) { 865 if (!AddMethodInfo(methodLiteral, method->GetJSPandaFile(), sequenceId)) { 866 continue; 867 } 868 } 869 AddTraceNodeId(methodLiteral); 870 } 871 872 TraceNode* topNode = traceTree_.AddNodeToTree(traceNodeIndex_); 873 if (topNode == nullptr) { 874 return -1; 875 } 876 ASSERT(topNode->GetTotalSize() <= static_cast<uint32_t>(INT_MAX)); 877 int totalSize = static_cast<int>(topNode->GetTotalSize()); 878 totalSize += size; 879 topNode->SetTotalSize(totalSize); 880 uint32_t totalCount = topNode->GetTotalCount(); 881 topNode->SetTotalCount(++totalCount); 882 return topNode->GetId(); 883} 884 885bool HeapSnapshot::AddMethodInfo(MethodLiteral *methodLiteral, 886 const JSPandaFile *jsPandaFile, 887 int sequenceId) 888{ 889 struct FunctionInfo codeEntry; 890 codeEntry.functionId = sequenceId; 891 panda_file::File::EntityId methodId = methodLiteral->GetMethodId(); 892 const std::string &functionName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId); 893 if (functionName.empty()) { 894 codeEntry.functionName = "anonymous"; 895 } else { 896 codeEntry.functionName = functionName; 897 } 898 GetString(codeEntry.functionName.c_str()); 899 900 // source file 901 DebugInfoExtractor *debugExtractor = 902 JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile); 903 const std::string &sourceFile = debugExtractor->GetSourceFile(methodId); 904 if (sourceFile.empty()) { 905 codeEntry.scriptName = ""; 906 } else { 907 codeEntry.scriptName = sourceFile; 908 auto iter = scriptIdMap_.find(codeEntry.scriptName); 909 if (iter == scriptIdMap_.end()) { 910 scriptIdMap_.emplace(codeEntry.scriptName, scriptIdMap_.size() + 1); 911 codeEntry.scriptId = static_cast<int>(scriptIdMap_.size()); 912 } else { 913 codeEntry.scriptId = iter->second; 914 } 915 } 916 GetString(codeEntry.scriptName.c_str()); 917 918 // line number 919 codeEntry.lineNumber = debugExtractor->GetFristLine(methodId); 920 // lineNumber is 0 means that lineTable error or empty function body, so jump this frame. 921 if (UNLIKELY(codeEntry.lineNumber == 0)) { 922 return false; 923 } 924 codeEntry.columnNumber = debugExtractor->GetFristColumn(methodId); 925 926 traceInfoStack_.emplace_back(codeEntry); 927 stackInfo_.emplace(methodLiteral, codeEntry); 928 return true; 929} 930 931Node *HeapSnapshot::GenerateStringNode(JSTaggedValue entry, size_t size, bool isInFinish, bool isBinMod) 932{ 933 static const CString EMPTY_STRING; 934 JSTaggedType addr = entry.GetRawData(); 935 Node *existNode = entryMap_.FindEntry(addr); // Fast Index 936 if (existNode != nullptr) { 937 if (isInFinish || isBinMod) { 938 existNode->SetName(GetString(EntryVisitor::ConvertKey(entry))); 939 } 940 existNode->SetLive(true); 941 return existNode; 942 } 943 // Allocation Event will generate string node for "". 944 // When we need to serialize and isFinish is true, the nodeName will be given the actual string content. 945 auto originStr = static_cast<EcmaString *>(entry.GetTaggedObject()); 946 size_t selfsize = (size != 0) ? size : EcmaStringAccessor(originStr).GetFlatStringSize(); 947 const CString *nodeName = &EMPTY_STRING; 948 if (isInFinish || isBinMod) { 949 nodeName = GetString(EntryVisitor::ConvertKey(entry)); 950 } 951 auto [idExist, sequenceId] = entryIdMap_->FindId(addr); 952 Node *node = Node::NewNode(chunk_, sequenceId, nodeCount_, nodeName, NodeType::STRING, selfsize, 953 0, addr); 954 if (!idExist) { 955 entryIdMap_->InsertId(addr, sequenceId); 956 } 957 entryMap_.InsertEntry(node); 958 InsertNodeUnique(node); 959 return node; 960} 961 962Node *HeapSnapshot::GeneratePrivateStringNode(size_t size) 963{ 964 if (privateStringNode_ != nullptr) { 965 return privateStringNode_; 966 } 967 JSTaggedValue stringValue = vm_->GetJSThread()->GlobalConstants()->GetStringString(); 968 auto originStr = static_cast<EcmaString *>(stringValue.GetTaggedObject()); 969 size_t selfsize = (size != 0) ? size : EcmaStringAccessor(originStr).GetFlatStringSize(); 970 CString strContent; 971 strContent.append(EntryVisitor::ConvertKey(stringValue)); 972 JSTaggedType addr = stringValue.GetRawData(); 973 auto [idExist, sequenceId] = entryIdMap_->FindId(addr); 974 Node *node = Node::NewNode(chunk_, sequenceId, nodeCount_, GetString(strContent), NodeType::STRING, selfsize, 975 0, addr); 976 if (!idExist) { 977 entryIdMap_->InsertId(addr, sequenceId); 978 } 979 entryMap_.InsertEntry(node); 980 InsertNodeUnique(node); 981 ASSERT(entryMap_.FindEntry(node->GetAddress())->GetAddress() == node->GetAddress()); 982 privateStringNode_ = node; 983 return node; 984} 985 986Node *HeapSnapshot::GenerateFunctionNode(JSTaggedValue entry, size_t size, bool isInFinish) 987{ 988 TaggedObject *obj = entry.GetTaggedObject(); 989 JSTaggedType addr = entry.GetRawData(); 990 Node *existNode = entryMap_.FindEntry(addr); 991 auto [idExist, sequenceId] = entryIdMap_->FindId(addr); 992 if (existNode != nullptr) { 993 if (isInFinish) { 994 existNode->SetName(GetString(ParseFunctionName(obj))); 995 } 996 existNode->SetLive(true); 997 return existNode; 998 } 999 size_t selfsize = (size != 0) ? size : obj->GetClass()->SizeFromJSHClass(obj); 1000 Node *node = Node::NewNode(chunk_, sequenceId, nodeCount_, GetString("JSFunction"), NodeType::CLOSURE, selfsize, 1001 0, addr); 1002 if (isInFinish) { 1003 node->SetName(GetString(ParseFunctionName(obj))); 1004 } 1005 if (!idExist) { 1006 entryIdMap_->InsertId(addr, sequenceId); 1007 } 1008 entryMap_.InsertEntry(node); 1009 InsertNodeUnique(node); 1010 return node; 1011} 1012 1013Node *HeapSnapshot::GenerateObjectNode(JSTaggedValue entry, size_t size, bool isInFinish) 1014{ 1015 TaggedObject *obj = entry.GetTaggedObject(); 1016 JSTaggedType addr = entry.GetRawData(); 1017 Node *existNode = entryMap_.FindEntry(addr); 1018 auto [idExist, sequenceId] = entryIdMap_->FindId(addr); 1019 if (existNode != nullptr) { 1020 if (isInFinish) { 1021 existNode->SetName(GetString(ParseObjectName(obj))); 1022 } 1023 existNode->SetLive(true); 1024 return existNode; 1025 } 1026 size_t selfsize = (size != 0) ? size : obj->GetClass()->SizeFromJSHClass(obj); 1027 Node *node = Node::NewNode(chunk_, sequenceId, nodeCount_, GetString("Object"), NodeType::OBJECT, selfsize, 1028 0, addr); 1029 if (isInFinish) { 1030 node->SetName(GetString(ParseObjectName(obj))); 1031 } 1032 if (!idExist) { 1033 entryIdMap_->InsertId(addr, sequenceId); 1034 } 1035 entryMap_.InsertEntry(node); 1036 InsertNodeUnique(node); 1037 return node; 1038} 1039 1040void HeapSnapshot::FillEdges(bool isSimplify) 1041{ 1042 auto iter = nodes_.begin(); 1043 size_t count = 0; 1044 while (count++ < nodes_.size()) { 1045 ASSERT(*iter != nullptr); 1046 auto entryFrom = *iter; 1047 JSTaggedValue value(entryFrom->GetAddress()); 1048 if (!value.IsHeapObject()) { 1049 iter++; 1050 continue; 1051 } 1052 std::vector<Reference> referenceResources; 1053 value.DumpForSnapshot(referenceResources, isVmMode_); 1054 for (auto const &it : referenceResources) { 1055 JSTaggedValue toValue = it.value_; 1056 if (toValue.IsNumber() && !captureNumericValue_) { 1057 continue; 1058 } 1059 Node *entryTo = nullptr; 1060 EdgeType type = toValue.IsWeak() ? EdgeType::WEAK : (EdgeType)it.type_; 1061 if (toValue.IsWeak()) { 1062 toValue.RemoveWeakTag(); 1063 } 1064 if (toValue.IsHeapObject()) { 1065 auto *to = toValue.GetTaggedObject(); 1066 entryTo = entryMap_.FindEntry(Node::NewAddress(to)); 1067 } 1068 if (entryTo == nullptr) { 1069 entryTo = GenerateNode(toValue, 0, true, isSimplify); 1070 } 1071 if (entryTo != nullptr) { 1072 Edge *edge = (it.type_ == Reference::ReferenceType::ELEMENT) ? 1073 Edge::NewEdge(chunk_, type, entryFrom, entryTo, it.index_) : 1074 Edge::NewEdge(chunk_, type, entryFrom, entryTo, GetString(it.name_)); 1075 RenameFunction(it.name_, entryFrom, entryTo); 1076 InsertEdgeUnique(edge); 1077 (*iter)->IncEdgeCount(); // Update Node's edgeCount_ here 1078 } 1079 } 1080 iter++; 1081 } 1082} 1083 1084void HeapSnapshot::FillEdgesForBinMod(RawHeapObjInfo *objInfo) 1085{ 1086 auto entryFrom = entryMap_.FindEntry(reinterpret_cast<JSTaggedType>(objInfo->newAddr)); 1087 JSTaggedValue value(entryFrom->GetAddress()); 1088 auto object = value.GetTaggedObject(); 1089 std::vector<Reference> referenceResources; 1090 auto jsHclass = object->GetClass(); 1091 if (jsHclass->IsJsGlobalEnv() || jsHclass->IsString()) { 1092 referenceResources.emplace_back("hclass", JSTaggedValue(jsHclass)); 1093 for (auto refAddr : objInfo->refSet) { 1094 JSTaggedValue val(refAddr); 1095 auto valTy = val.GetTaggedObject()->GetClass()->GetObjectType(); 1096 referenceResources.emplace_back(JSHClass::DumpJSType(valTy), val); 1097 } 1098 } else { 1099 value.DumpForSnapshot(referenceResources, false); 1100 } 1101 for (auto const &it : referenceResources) { 1102 JSTaggedValue toValue = it.value_; 1103 if (toValue.IsNumber() && !captureNumericValue_) { 1104 continue; 1105 } 1106 Node *entryTo = nullptr; 1107 EdgeType type = toValue.IsWeak() ? EdgeType::WEAK : (EdgeType)it.type_; 1108 if (toValue.IsWeak()) { 1109 toValue.RemoveWeakTag(); 1110 } 1111 if (toValue.IsHeapObject()) { 1112 auto *to = toValue.GetTaggedObject(); 1113 entryTo = entryMap_.FindEntry(Node::NewAddress(to)); 1114 } 1115 if (entryTo == nullptr) { 1116 CString name; 1117 name.append("MissObj").append(std::to_string(toValue.GetRawData())); 1118 Node *missObj = Node::NewNode(chunk_, 1, nodeCount_, GetString(name), NodeType::OBJECT, 0, 0, 0); 1119 entryMap_.InsertEntry(missObj); 1120 InsertNodeUnique(missObj); 1121 } 1122 if (entryTo != nullptr) { 1123 Edge *edge = (it.type_ == Reference::ReferenceType::ELEMENT) ? 1124 Edge::NewEdge(chunk_, type, entryFrom, entryTo, it.index_) : 1125 Edge::NewEdge(chunk_, type, entryFrom, entryTo, GetString(it.name_)); 1126 RenameFunction(it.name_, entryFrom, entryTo); 1127 InsertEdgeUnique(edge); 1128 entryFrom->IncEdgeCount(); // Update Node's edgeCount_ here 1129 } 1130 } 1131} 1132 1133void HeapSnapshot::AddSyntheticRootForBinMod(RawHeapObjInfo *objInfo, int &edgeOffset, Node *syntheticRoot) 1134{ 1135 if (!objInfo->isRoot) { 1136 return; 1137 } 1138 TaggedObject *root = reinterpret_cast<TaggedObject *>(objInfo->newAddr); 1139 Node *rootNode = entryMap_.FindEntry(Node::NewAddress(root)); 1140 if (rootNode != nullptr) { 1141 Edge *edge = Edge::NewEdge(chunk_, 1142 EdgeType::SHORTCUT, syntheticRoot, rootNode, GetString("-subroot-")); 1143 InsertEdgeAt(edgeOffset, edge); 1144 edgeOffset++; 1145 syntheticRoot->IncEdgeCount(); 1146 } 1147} 1148 1149Node *HeapSnapshot::GenerateNodeForBinMod(TaggedObject *obj, RawHeapObjInfo *objInfo, 1150 CUnorderedMap<uint64_t, const char *> &strTableIdMap) 1151{ 1152 auto currNode = GenerateNode(JSTaggedValue(obj), objInfo->tInfo->objSize, false, false, true); 1153 if (strTableIdMap.find(objInfo->tInfo->stringId) != strTableIdMap.end() 1154 && strTableIdMap[objInfo->tInfo->stringId] != nullptr) { 1155 if (currNode != nullptr) { 1156 currNode->SetName(GetString(strTableIdMap[objInfo->tInfo->stringId])); 1157 } 1158 } 1159 return currNode; 1160} 1161 1162bool HeapSnapshot::BuildSnapshotForBinMod(CVector<RawHeapObjInfo *> &objInfoVec) 1163{ 1164 Node *syntheticRoot = Node::NewNode(chunk_, 1, nodeCount_, GetString("SyntheticRoot"), 1165 NodeType::SYNTHETIC, 0, 0, 0); 1166 InsertNodeAt(0, syntheticRoot); 1167 int edgeOffset = 0; 1168 for (auto objInfo : objInfoVec) { 1169 FillEdgesForBinMod(objInfo); 1170 AddSyntheticRootForBinMod(objInfo, edgeOffset, syntheticRoot); 1171 } 1172 int reindex = 0; 1173 for (Node *node : nodes_) { 1174 node->SetIndex(reindex); 1175 reindex++; 1176 } 1177 return Verify(); 1178} 1179 1180void HeapSnapshot::RenameFunction(const CString &edgeName, Node *entryFrom, Node *entryTo) 1181{ 1182 if (edgeName != "name") { 1183 return; 1184 } 1185 if (entryFrom->GetType() != NodeType::CLOSURE) { 1186 return; 1187 } 1188 if (*entryFrom->GetName() == "JSFunction" && *entryTo->GetName() != "" && 1189 *entryTo->GetName() != "InternalAccessor") { 1190 entryFrom->SetName(GetString(*entryTo->GetName())); 1191 } 1192} 1193 1194CString HeapSnapshot::ParseFunctionName(TaggedObject *obj) 1195{ 1196 CString result; 1197 JSFunctionBase *func = JSFunctionBase::Cast(obj); 1198 Method *method = Method::Cast(func->GetMethod().GetTaggedObject()); 1199 MethodLiteral *methodLiteral = method->GetMethodLiteral(); 1200 if (methodLiteral == nullptr) { 1201 return "JSFunction"; 1202 } 1203 const JSPandaFile *jsPandaFile = method->GetJSPandaFile(); 1204 panda_file::File::EntityId methodId = methodLiteral->GetMethodId(); 1205 const CString &nameStr = MethodLiteral::ParseFunctionNameToCString(jsPandaFile, methodId); 1206 const CString &moduleStr = method->GetRecordNameStr(); 1207 1208 if (!moduleStr.empty()) { 1209 result.append(moduleStr).append(" "); 1210 } 1211 if (nameStr.empty()) { 1212 result.append("anonymous"); 1213 } else { 1214 result.append(nameStr); 1215 } 1216 DebugInfoExtractor *debugExtractor = 1217 JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile); 1218 if (debugExtractor == nullptr) { 1219 return result; 1220 } 1221 int32_t line = debugExtractor->GetFristLine(methodId); 1222 return result.append("(line:").append(std::to_string(line)).append(")"); 1223} 1224 1225const CString HeapSnapshot::ParseObjectName(TaggedObject *obj) 1226{ 1227 ASSERT(JSTaggedValue(obj).IsJSObject()); 1228 JSThread *thread = vm_->GetJSThread(); 1229 bool isCallGetter = false; 1230 return JSObject::ExtractConstructorAndRecordName(thread, obj, true, &isCallGetter); 1231} 1232 1233Node *HeapSnapshot::InsertNodeUnique(Node *node) 1234{ 1235 AccumulateNodeSize(node->GetSelfSize()); 1236 nodes_.emplace_back(node); 1237 nodeCount_++; 1238 return node; 1239} 1240 1241void HeapSnapshot::EraseNodeUnique(Node *node) 1242{ 1243 auto iter = std::find(nodes_.begin(), nodes_.end(), node); 1244 if (iter != nodes_.end()) { 1245 DecreaseNodeSize(node->GetSelfSize()); 1246 chunk_->Delete(node); 1247 nodes_.erase(iter); 1248 nodeCount_--; 1249 } 1250} 1251 1252Edge *HeapSnapshot::InsertEdgeUnique(Edge *edge) 1253{ 1254 edges_.emplace_back(edge); 1255 edgeCount_++; 1256 return edge; 1257} 1258 1259void HeapSnapshot::AddSyntheticRoot() 1260{ 1261 Node *syntheticRoot = Node::NewNode(chunk_, 1, nodeCount_, GetString("SyntheticRoot"), 1262 NodeType::SYNTHETIC, 0, 0, 0); 1263 InsertNodeAt(0, syntheticRoot); 1264 CUnorderedSet<JSTaggedType> values {}; 1265 int edgeOffset = 0; 1266// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1267#define ROOT_EDGE_BUILDER_CORE(type, slot) \ 1268 do { \ 1269 JSTaggedValue value((slot).GetTaggedType()); \ 1270 if (value.IsHeapObject()) { \ 1271 TaggedObject *root = value.GetTaggedObject(); \ 1272 Node *rootNode = entryMap_.FindEntry(Node::NewAddress(root)); \ 1273 if (rootNode != nullptr) { \ 1274 JSTaggedType valueTo = value.GetRawData(); \ 1275 auto it = values.find(valueTo); \ 1276 if (it == values.end()) { \ 1277 values.insert(valueTo); \ 1278 Edge *edge = Edge::NewEdge(chunk_, \ 1279 EdgeType::SHORTCUT, syntheticRoot, rootNode, GetString("-subroot-")); \ 1280 InsertEdgeAt(edgeOffset, edge); \ 1281 edgeOffset++; \ 1282 syntheticRoot->IncEdgeCount(); \ 1283 } \ 1284 } \ 1285 } \ 1286 } while (false) 1287 1288 RootVisitor rootEdgeBuilder = [this, syntheticRoot, &edgeOffset, &values]( 1289 [[maybe_unused]] Root type, ObjectSlot slot) { 1290 ROOT_EDGE_BUILDER_CORE(type, slot); 1291 }; 1292 RootBaseAndDerivedVisitor rootBaseEdgeBuilder = [] 1293 ([[maybe_unused]] Root type, [[maybe_unused]] ObjectSlot base, [[maybe_unused]] ObjectSlot derived, 1294 [[maybe_unused]] uintptr_t baseOldObject) { 1295 }; 1296 1297 RootRangeVisitor rootRangeEdgeBuilder = [this, syntheticRoot, &edgeOffset, &values]([[maybe_unused]] Root type, 1298 ObjectSlot start, ObjectSlot end) { 1299 for (ObjectSlot slot = start; slot < end; slot++) { 1300 ROOT_EDGE_BUILDER_CORE(type, slot); 1301 } 1302 }; 1303#undef ROOT_EDGE_BUILDER_CORE 1304 rootVisitor_.VisitHeapRoots(vm_->GetJSThread(), rootEdgeBuilder, rootRangeEdgeBuilder, rootBaseEdgeBuilder); 1305 1306 int reindex = 0; 1307 for (Node *node : nodes_) { 1308 node->SetIndex(reindex); 1309 reindex++; 1310 } 1311} 1312 1313Node *HeapSnapshot::InsertNodeAt(size_t pos, Node *node) 1314{ 1315 ASSERT(node != nullptr); 1316 auto iter = nodes_.begin(); 1317 std::advance(iter, static_cast<int>(pos)); 1318 nodes_.insert(iter, node); 1319 nodeCount_++; 1320 return node; 1321} 1322 1323Edge *HeapSnapshot::InsertEdgeAt(size_t pos, Edge *edge) 1324{ 1325 ASSERT(edge != nullptr); 1326 auto iter = edges_.begin(); 1327 std::advance(iter, static_cast<int>(pos)); 1328 edges_.insert(iter, edge); 1329 edgeCount_++; 1330 return edge; 1331} 1332 1333CString EntryVisitor::ConvertKey(JSTaggedValue key) 1334{ 1335 ASSERT(key.GetTaggedObject() != nullptr); 1336 EcmaString *keyString = EcmaString::Cast(key.GetTaggedObject()); 1337 1338 if (key.IsSymbol()) { 1339 JSSymbol *symbol = JSSymbol::Cast(key.GetTaggedObject()); 1340 keyString = EcmaString::Cast(symbol->GetDescription().GetTaggedObject()); 1341 } 1342 // convert, expensive but safe 1343 return EcmaStringAccessor(keyString).ToCString(StringConvertedUsage::PRINT); 1344} 1345 1346Node *HeapEntryMap::FindOrInsertNode(Node *node) 1347{ 1348 ASSERT(node != nullptr); 1349 auto it = nodesMap_.find(node->GetAddress()); 1350 if (it != nodesMap_.end()) { 1351 return it->second; 1352 } 1353 InsertEntry(node); 1354 return node; 1355} 1356 1357Node *HeapEntryMap::FindAndEraseNode(JSTaggedType addr) 1358{ 1359 auto it = nodesMap_.find(addr); 1360 if (it != nodesMap_.end()) { 1361 Node *node = it->second; 1362 nodesMap_.erase(it); 1363 return node; 1364 } 1365 return nullptr; 1366} 1367 1368Node *HeapEntryMap::FindEntry(JSTaggedType addr) 1369{ 1370 auto it = nodesMap_.find(addr); 1371 return it != nodesMap_.end() ? it->second : nullptr; 1372} 1373 1374void HeapEntryMap::InsertEntry(Node *node) 1375{ 1376 nodesMap_.emplace(node->GetAddress(), node); 1377} 1378} // namespace panda::ecmascript 1379