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