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