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