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