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