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