• 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     stringTable_ = nullptr;
85 }
86 
BuildUp(bool isSimplify)87 bool HeapSnapshot::BuildUp(bool isSimplify)
88 {
89     FillNodes(true, isSimplify);
90     FillEdges(isSimplify);
91     AddSyntheticRoot();
92     return Verify();
93 }
94 
Verify()95 bool HeapSnapshot::Verify()
96 {
97     GetString(CString("HeapVerify:").append(ToCString(totalNodesSize_)));
98     return (edgeCount_ > nodeCount_) && (totalNodesSize_ > 0);
99 }
100 
PrepareSnapshot()101 void HeapSnapshot::PrepareSnapshot()
102 {
103     FillNodes();
104     if (trackAllocations()) {
105         PrepareTraceInfo();
106     }
107 }
108 
UpdateNodes(bool isInFinish)109 void HeapSnapshot::UpdateNodes(bool isInFinish)
110 {
111     for (Node *node : nodes_) {
112         node->SetLive(false);
113     }
114     FillNodes(isInFinish);
115     for (auto iter = nodes_.begin(); iter != nodes_.end();) {
116         if (!(*iter)->IsLive()) {
117             entryMap_.FindAndEraseNode((*iter)->GetAddress());
118             entryIdMap_->EraseId((*iter)->GetAddress());
119             DecreaseNodeSize((*iter)->GetSelfSize());
120             chunk_.Delete(*iter);
121             iter = nodes_.erase(iter);
122             nodeCount_--;
123         } else {
124             iter++;
125         }
126     }
127 }
128 
FinishSnapshot()129 bool HeapSnapshot::FinishSnapshot()
130 {
131     UpdateNodes(true);
132     FillEdges();
133     AddSyntheticRoot();
134     return Verify();
135 }
136 
RecordSampleTime()137 void HeapSnapshot::RecordSampleTime()
138 {
139     timeStamps_.emplace_back(entryIdMap_->GetLastId());
140 }
141 
PushHeapStat(Stream * stream)142 void HeapSnapshot::PushHeapStat(Stream* stream)
143 {
144     CVector<HeapStat> statsBuffer;
145     if (stream == nullptr) {
146         LOG_DEBUGGER(ERROR) << "HeapSnapshot::PushHeapStat::stream is nullptr";
147         return;
148     }
149     int32_t preChunkSize = stream->GetSize();
150     int32_t sequenceId = 0;
151     int64_t timeStampUs = 0;
152     auto iter = nodes_.begin();
153     for (size_t timeIndex = 0; timeIndex < timeStamps_.size(); ++timeIndex) {
154         TimeStamp& timeStamp = timeStamps_[timeIndex];
155         sequenceId = timeStamp.GetLastSequenceId();
156         timeStampUs = timeStamp.GetTimeStamp();
157         uint32_t nodesSize = 0;
158         uint32_t nodesCount = 0;
159         while (iter != nodes_.end() && (*iter)->GetId() <= static_cast<uint32_t>(sequenceId)) {
160             nodesCount++;
161             nodesSize += (*iter)->GetSelfSize();
162             iter++;
163         }
164         if ((timeStamp.GetCount() != nodesCount) || (timeStamp.GetSize() != nodesSize)) {
165             timeStamp.SetCount(nodesCount);
166             timeStamp.SetSize(nodesSize);
167             statsBuffer.emplace_back(static_cast<int32_t>(timeIndex), nodesCount, nodesSize);
168             if (static_cast<int32_t>(statsBuffer.size()) >= preChunkSize) {
169                 stream->UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size()));
170                 statsBuffer.clear();
171             }
172         }
173     }
174     if (!statsBuffer.empty()) {
175         stream->UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size()));
176         statsBuffer.clear();
177     }
178     stream->UpdateLastSeenObjectId(sequenceId, timeStampUs);
179 }
180 
AddNode(TaggedObject * address,size_t size)181 Node *HeapSnapshot::AddNode(TaggedObject *address, size_t size)
182 {
183     return GenerateNode(JSTaggedValue(address), size);
184 }
185 
MoveNode(uintptr_t address,TaggedObject * forwardAddress,size_t size)186 void HeapSnapshot::MoveNode(uintptr_t address, TaggedObject *forwardAddress, size_t size)
187 {
188     if (address == reinterpret_cast<uintptr_t>(forwardAddress)) {
189         return;
190     }
191 
192     Node *node = entryMap_.FindAndEraseNode(static_cast<JSTaggedType>(address));
193     if (node != nullptr) {
194         ASSERT(node->GetId() <= UINT_MAX);
195 
196         Node *oldNode = entryMap_.FindAndEraseNode(Node::NewAddress(forwardAddress));
197         if (oldNode != nullptr) {
198             oldNode->SetAddress(Node::NewAddress(TaggedObject::Cast(nullptr)));
199         }
200 
201         // Size and name may change during its life for some types(such as string, array and etc).
202         if (forwardAddress->GetClass() != nullptr) {
203             node->SetName(GenerateNodeName(forwardAddress));
204         }
205         if (JSTaggedValue(forwardAddress).IsString()) {
206             node->SetSelfSize(forwardAddress->GetClass()->SizeFromJSHClass(forwardAddress));
207         } else {
208             node->SetSelfSize(size);
209         }
210         node->SetAddress(Node::NewAddress(forwardAddress));
211         entryMap_.InsertEntry(node);
212     } else {
213         LOG_DEBUGGER(WARN) << "Untracked object moves from " << address << " to " << forwardAddress;
214         GenerateNode(JSTaggedValue(forwardAddress), size, false);
215     }
216 }
217 
218 // NOLINTNEXTLINE(readability-function-size)
GenerateNodeName(TaggedObject * entry)219 CString *HeapSnapshot::GenerateNodeName(TaggedObject *entry)
220 {
221     auto *hCls = entry->GetClass();
222     JSType type = hCls->GetObjectType();
223     switch (type) {
224         case JSType::TAGGED_ARRAY:
225             return GetArrayString(TaggedArray::Cast(entry), "ArkInternalArray[");
226         case JSType::LEXICAL_ENV:
227             return GetArrayString(TaggedArray::Cast(entry), "LexicalEnv[");
228         case JSType::SENDABLE_ENV:
229             return GetArrayString(TaggedArray::Cast(entry), "SendableEnv[");
230         case JSType::CONSTANT_POOL:
231             return GetArrayString(TaggedArray::Cast(entry), "ArkInternalConstantPool[");
232         case JSType::PROFILE_TYPE_INFO:
233             return GetArrayString(TaggedArray::Cast(entry), "ArkInternalProfileTypeInfo[");
234         case JSType::TAGGED_DICTIONARY:
235             return GetArrayString(TaggedArray::Cast(entry), "ArkInternalDict[");
236         case JSType::AOT_LITERAL_INFO:
237             return GetArrayString(TaggedArray::Cast(entry), "ArkInternalAOTLiteralInfo[");
238         case JSType::VTABLE:
239             return GetArrayString(TaggedArray::Cast(entry), "ArkInternalVTable[");
240         case JSType::COW_TAGGED_ARRAY:
241             return GetArrayString(TaggedArray::Cast(entry), "ArkInternalCOWArray[");
242         case JSType::HCLASS:
243             return GetString("HiddenClass(NonMovable)");
244         case JSType::LINKED_NODE:
245             return GetString("LinkedNode");
246         case JSType::TRACK_INFO:
247             return GetString("TrackInfo");
248         case JSType::LINE_STRING:
249         case JSType::CONSTANT_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         default:
558             break;
559     }
560     if (IsInVmMode()) {
561         switch (type) {
562             case JSType::PROPERTY_BOX:
563                 return GetString("PropertyBox");
564             case JSType::GLOBAL_ENV:
565                 return GetString("GlobalEnv");
566             case JSType::PROTOTYPE_HANDLER:
567                 return GetString("ProtoTypeHandler");
568             case JSType::TRANSITION_HANDLER:
569                 return GetString("TransitionHandler");
570             case JSType::TRANS_WITH_PROTO_HANDLER:
571                 return GetString("TransWithProtoHandler");
572             case JSType::STORE_TS_HANDLER:
573                 return GetString("StoreTSHandler");
574             case JSType::PROTO_CHANGE_MARKER:
575                 return GetString("ProtoChangeMarker");
576             case JSType::MARKER_CELL:
577                 return GetString("MarkerCell");
578             case JSType::PROTOTYPE_INFO:
579                 return GetString("ProtoChangeDetails");
580             case JSType::TEMPLATE_MAP:
581                 return GetString("TemplateMap");
582             case JSType::PROGRAM:
583                 return GetString("Program");
584             case JSType::MACHINE_CODE_OBJECT:
585                 return GetString("MachineCode");
586             case JSType::CLASS_INFO_EXTRACTOR:
587                 return GetString("ClassInfoExtractor");
588             default:
589                 break;
590         }
591     } else {
592         return GetString("Hidden Object");
593     }
594     return GetString(CString("UnKnownType").append(std::to_string(static_cast<int>(type))));
595 }
596 
GenerateNodeType(TaggedObject * entry)597 NodeType HeapSnapshot::GenerateNodeType(TaggedObject *entry)
598 {
599     NodeType nodeType = NodeType::DEFAULT;
600     auto *hCls = entry->GetClass();
601     JSType type = hCls->GetObjectType();
602 
603     if (hCls->IsTaggedArray()) {
604         nodeType = NodeType::ARRAY;
605     } else if (hCls->IsHClass()) {
606         nodeType = NodeType::DEFAULT;
607     } else if (type == JSType::PROPERTY_BOX) {
608         nodeType = NodeType::HIDDEN;
609     } else if (type == JSType::JS_ARRAY || type == JSType::JS_TYPED_ARRAY) {
610         nodeType = NodeType::OBJECT;
611     } else if (type == JSType::JS_OBJECT || type == JSType::JS_SHARED_OBJECT) {
612         nodeType = NodeType::OBJECT;
613     } else if (type >= JSType::JS_FUNCTION_FIRST && type <= JSType::JS_FUNCTION_LAST) {
614         nodeType = NodeType::CLOSURE;
615     } else if (type == JSType::JS_BOUND_FUNCTION) {
616         nodeType = NodeType::DEFAULT;
617     } else if (type == JSType::JS_FUNCTION_BASE) {
618         nodeType = NodeType::DEFAULT;
619     } else if (type == JSType::JS_REG_EXP) {
620         nodeType = NodeType::REGEXP;
621     } else if (type == JSType::SYMBOL) {
622         nodeType = NodeType::SYMBOL;
623     } else if (type == JSType::JS_PRIMITIVE_REF) {
624         nodeType = NodeType::HEAPNUMBER;
625     } else if (type == JSType::BIGINT) {
626         nodeType = NodeType::BIGINT;
627     } else {
628         nodeType = NodeType::DEFAULT;
629     }
630 
631     return nodeType;
632 }
633 
FillNodes(bool isInFinish,bool isSimplify)634 void HeapSnapshot::FillNodes(bool isInFinish, bool isSimplify)
635 {
636     LOG_ECMA(INFO) << "HeapSnapshot::FillNodes";
637     ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "HeapSnapshot::FillNodes");
638     // Iterate Heap Object
639     auto heap = vm_->GetHeap();
640     if (heap != nullptr) {
641         heap->IterateOverObjects([this, isInFinish, isSimplify](TaggedObject *obj) {
642             GenerateNode(JSTaggedValue(obj), 0, isInFinish, isSimplify);
643         }, isSimplify);
644     }
645 }
646 
HandleStringNode(JSTaggedValue & entry,size_t & size,bool & isInFinish,bool isBinMod)647 Node *HeapSnapshot::HandleStringNode(JSTaggedValue &entry, size_t &size, bool &isInFinish, bool isBinMod)
648 {
649     Node* node = nullptr;
650     if (isPrivate_) {
651         node = GeneratePrivateStringNode(size);
652     } else {
653         node = GenerateStringNode(entry, size, isInFinish, isBinMod);
654     }
655     if (node == nullptr) {
656         LOG_ECMA(ERROR) << "string node nullptr";
657     }
658     return node;
659 }
660 
HandleFunctionNode(JSTaggedValue & entry,size_t & size,bool & isInFinish)661 Node *HeapSnapshot::HandleFunctionNode(JSTaggedValue &entry, size_t &size, bool &isInFinish)
662 {
663     Node* node = GenerateFunctionNode(entry, size, isInFinish);
664     if (node == nullptr) {
665         LOG_ECMA(ERROR) << "function node nullptr";
666     }
667     return node;
668 }
669 
HandleObjectNode(JSTaggedValue & entry,size_t & size,bool & isInFinish)670 Node *HeapSnapshot::HandleObjectNode(JSTaggedValue &entry, size_t &size, bool &isInFinish)
671 {
672     Node* node = GenerateObjectNode(entry, size, isInFinish);
673     if (node == nullptr) {
674         LOG_ECMA(ERROR) << "object node nullptr";
675     }
676     return node;
677 }
678 
HandleBaseClassNode(size_t size,bool idExist,NodeId & sequenceId,TaggedObject * obj,JSTaggedType & addr)679 Node *HeapSnapshot::HandleBaseClassNode(size_t size, bool idExist, NodeId &sequenceId,
680                                         TaggedObject* obj, JSTaggedType &addr)
681 {
682     size_t selfSize = (size != 0) ? size : obj->GetClass()->SizeFromJSHClass(obj);
683     size_t nativeSize = 0;
684     if (obj->GetClass()->IsJSNativePointer()) {
685         nativeSize = JSNativePointer::Cast(obj)->GetBindingSize();
686     }
687     Node* node = Node::NewNode(chunk_, sequenceId, nodeCount_, GenerateNodeName(obj), GenerateNodeType(obj),
688         selfSize, nativeSize, addr);
689     entryMap_.InsertEntry(node);
690     if (!idExist) {
691         entryIdMap_->InsertId(addr, sequenceId);
692     }
693     InsertNodeUnique(node);
694     ASSERT(entryMap_.FindEntry(node->GetAddress())->GetAddress() == node->GetAddress());
695     return node;
696 }
697 
GeneratePrimitiveNameString(JSTaggedValue & entry)698 CString HeapSnapshot::GeneratePrimitiveNameString(JSTaggedValue &entry)
699 {
700     CString primitiveName;
701     if (entry.IsInt()) {
702         primitiveName.append("Int:");
703         if (!isPrivate_) {
704             primitiveName.append(ToCString(entry.GetInt()));
705         }
706     } else if (entry.IsDouble()) {
707         primitiveName.append("Double:");
708         if (!isPrivate_) {
709             primitiveName.append(FloatToCString(entry.GetDouble()));
710         }
711     } else if (entry.IsHole()) {
712         primitiveName.append("Hole");
713     } else if (entry.IsNull()) {
714         primitiveName.append("Null");
715     } else if (entry.IsTrue()) {
716         primitiveName.append("Boolean:true");
717     } else if (entry.IsFalse()) {
718         primitiveName.append("Boolean:false");
719     } else if (entry.IsException()) {
720         primitiveName.append("Exception");
721     } else if (entry.IsUndefined()) {
722         primitiveName.append("Undefined");
723     } else {
724         primitiveName.append("Illegal_Primitive");
725     }
726     return primitiveName;
727 }
728 
GenerateNode(JSTaggedValue entry,size_t size,bool isInFinish,bool isSimplify,bool isBinMod)729 Node *HeapSnapshot::GenerateNode(JSTaggedValue entry, size_t size, bool isInFinish, bool isSimplify, bool isBinMod)
730 {
731     Node *node = nullptr;
732     if (entry.IsHeapObject()) {
733         if (entry.IsWeak()) {
734             entry.RemoveWeakTag();
735         }
736         if (entry.IsString()) {
737             return HandleStringNode(entry, size, isInFinish, isBinMod);
738         }
739         if (entry.IsJSFunction()) {
740             return HandleFunctionNode(entry, size, isInFinish);
741         }
742         if (entry.IsOnlyJSObject()) {
743             return HandleObjectNode(entry, size, isInFinish);
744         }
745         TaggedObject *obj = entry.GetTaggedObject();
746         auto *baseClass = obj->GetClass();
747         if (baseClass != nullptr) {
748             JSTaggedType addr = entry.GetRawData();
749             Node *existNode = entryMap_.FindEntry(addr);  // Fast Index
750             auto [idExist, sequenceId] = entryIdMap_->FindId(addr);
751             if (existNode == nullptr) {
752                 return HandleBaseClassNode(size, idExist, sequenceId, obj, addr);
753             } else {
754                 existNode->SetLive(true);
755                 return existNode;
756             }
757         }
758     } else if (!isSimplify) {
759         if ((entry.IsInt() || entry.IsDouble()) && !captureNumericValue_) {
760                 return nullptr;
761         }
762         CString primitiveName = GeneratePrimitiveNameString(entry);
763         // A primitive value with tag will be regarded as a pointer
764         JSTaggedType addr = entry.GetRawData();
765         Node *existNode = entryMap_.FindEntry(addr);
766         if (existNode != nullptr) {
767             existNode->SetLive(true);
768             return existNode;
769         }
770         auto [idExist, sequenceId] = entryIdMap_->FindId(addr);
771         node = Node::NewNode(chunk_, sequenceId, nodeCount_, GetString(primitiveName), NodeType::HEAPNUMBER, 0,
772                              0, addr);
773         entryMap_.InsertEntry(node);  // Fast Index
774         if (!idExist) {
775             entryIdMap_->InsertId(addr, sequenceId);
776         }
777         InsertNodeUnique(node);
778     }
779     return node;
780 }
781 
TraceNode(TraceTree * tree,uint32_t nodeIndex)782 TraceNode::TraceNode(TraceTree* tree, uint32_t nodeIndex)
783     : tree_(tree),
784       nodeIndex_(nodeIndex),
785       totalSize_(0),
786       totalCount_(0),
787       id_(tree->GetNextNodeId())
788 {
789 }
790 
~TraceNode()791 TraceNode::~TraceNode()
792 {
793     for (TraceNode* node : children_) {
794         delete node;
795     }
796     children_.clear();
797 }
798 
AddNodeToTree(CVector<uint32_t> traceNodeIndex)799 TraceNode* TraceTree::AddNodeToTree(CVector<uint32_t> traceNodeIndex)
800 {
801     uint32_t len = traceNodeIndex.size();
802     if (len == 0) {
803         return nullptr;
804     }
805 
806     TraceNode* node = GetRoot();
807     for (int i = static_cast<int>(len) - 1; i >= 0; i--) {
808         node = node->FindOrAddChild(traceNodeIndex[i]);
809     }
810     return node;
811 }
812 
FindOrAddChild(uint32_t nodeIndex)813 TraceNode* TraceNode::FindOrAddChild(uint32_t nodeIndex)
814 {
815     TraceNode* child = FindChild(nodeIndex);
816     if (child == nullptr) {
817         child = new TraceNode(tree_, nodeIndex);
818         children_.push_back(child);
819     }
820     return child;
821 }
822 
FindChild(uint32_t nodeIndex)823 TraceNode* TraceNode::FindChild(uint32_t nodeIndex)
824 {
825     for (TraceNode* node : children_) {
826         if (node->GetNodeIndex() == nodeIndex) {
827             return node;
828         }
829     }
830     return nullptr;
831 }
832 
AddTraceNodeId(MethodLiteral * methodLiteral)833 void HeapSnapshot::AddTraceNodeId(MethodLiteral *methodLiteral)
834 {
835     uint32_t traceNodeId = 0;
836     auto result = methodToTraceNodeId_.find(methodLiteral);
837     if (result == methodToTraceNodeId_.end()) {
838         ASSERT(traceInfoStack_.size() > 0);
839         traceNodeId = traceInfoStack_.size() - 1;
840         methodToTraceNodeId_.emplace(methodLiteral, traceNodeId);
841     } else {
842         traceNodeId = result->second;
843     }
844     traceNodeIndex_.emplace_back(traceNodeId);
845 }
846 
AddTraceNode(int sequenceId,int size)847 int HeapSnapshot::AddTraceNode(int sequenceId, int size)
848 {
849     traceNodeIndex_.clear();
850     auto thread = vm_->GetJSThread();
851     JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetCurrentFrame());
852     FrameIterator it(current, thread);
853     for (; !it.Done(); it.Advance<GCVisitedFlag::VISITED>()) {
854         if (!it.IsJSFrame()) {
855             continue;
856         }
857         auto method = it.CheckAndGetMethod();
858         if (method == nullptr || method->IsNativeWithCallField()) {
859             continue;
860         }
861         MethodLiteral *methodLiteral = method->GetMethodLiteral();
862         if (methodLiteral == nullptr) {
863             continue;
864         }
865         if (stackInfo_.count(methodLiteral) == 0) {
866             if (!AddMethodInfo(methodLiteral, method->GetJSPandaFile(), sequenceId)) {
867                 continue;
868             }
869         }
870         AddTraceNodeId(methodLiteral);
871     }
872 
873     TraceNode* topNode = traceTree_.AddNodeToTree(traceNodeIndex_);
874     if (topNode == nullptr) {
875         return -1;
876     }
877     ASSERT(topNode->GetTotalSize() <= static_cast<uint32_t>(INT_MAX));
878     int totalSize = static_cast<int>(topNode->GetTotalSize());
879     totalSize += size;
880     topNode->SetTotalSize(totalSize);
881     uint32_t totalCount = topNode->GetTotalCount();
882     topNode->SetTotalCount(++totalCount);
883     return topNode->GetId();
884 }
885 
AddMethodInfo(MethodLiteral * methodLiteral,const JSPandaFile * jsPandaFile,int sequenceId)886 bool HeapSnapshot::AddMethodInfo(MethodLiteral *methodLiteral,
887                                  const JSPandaFile *jsPandaFile,
888                                  int sequenceId)
889 {
890     struct FunctionInfo codeEntry;
891     codeEntry.functionId = sequenceId;
892     panda_file::File::EntityId methodId = methodLiteral->GetMethodId();
893     const std::string &functionName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
894     if (functionName.empty()) {
895         codeEntry.functionName = "anonymous";
896     } else {
897         codeEntry.functionName = functionName;
898     }
899     GetString(codeEntry.functionName.c_str());
900 
901     // source file
902     DebugInfoExtractor *debugExtractor =
903         JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile);
904     const std::string &sourceFile = debugExtractor->GetSourceFile(methodId);
905     if (sourceFile.empty()) {
906         codeEntry.scriptName = "";
907     } else {
908         codeEntry.scriptName = sourceFile;
909         auto iter = scriptIdMap_.find(codeEntry.scriptName);
910         if (iter == scriptIdMap_.end()) {
911             scriptIdMap_.emplace(codeEntry.scriptName, scriptIdMap_.size() + 1);
912             codeEntry.scriptId = static_cast<int>(scriptIdMap_.size());
913         } else {
914             codeEntry.scriptId = iter->second;
915         }
916     }
917     GetString(codeEntry.scriptName.c_str());
918 
919     // line number
920     codeEntry.lineNumber = debugExtractor->GetFristLine(methodId);
921     // lineNumber is 0 means that lineTable error or empty function body, so jump this frame.
922     if (UNLIKELY(codeEntry.lineNumber == 0)) {
923         return false;
924     }
925     codeEntry.columnNumber = debugExtractor->GetFristColumn(methodId);
926 
927     traceInfoStack_.emplace_back(codeEntry);
928     stackInfo_.emplace(methodLiteral, codeEntry);
929     return true;
930 }
931 
GenerateStringNode(JSTaggedValue entry,size_t size,bool isInFinish,bool isBinMod)932 Node *HeapSnapshot::GenerateStringNode(JSTaggedValue entry, size_t size, bool isInFinish, bool isBinMod)
933 {
934     static const CString EMPTY_STRING;
935     JSTaggedType addr = entry.GetRawData();
936     Node *existNode = entryMap_.FindEntry(addr);  // Fast Index
937     if (existNode != nullptr) {
938         if (isInFinish || isBinMod) {
939             existNode->SetName(GetString(EntryVisitor::ConvertKey(entry)));
940         }
941         existNode->SetLive(true);
942         return existNode;
943     }
944     // Allocation Event will generate string node for "".
945     // When we need to serialize and isFinish is true, the nodeName will be given the actual string content.
946     size_t selfsize = (size != 0) ? size :
947         entry.GetTaggedObject()->GetClass()->SizeFromJSHClass(entry.GetTaggedObject());
948     const CString *nodeName = &EMPTY_STRING;
949     if (isInFinish || isBinMod) {
950         nodeName = GetString(EntryVisitor::ConvertKey(entry));
951     }
952     auto [idExist, sequenceId] = entryIdMap_->FindId(addr);
953     Node *node = Node::NewNode(chunk_, sequenceId, nodeCount_, nodeName, NodeType::STRING, selfsize,
954                                0, addr);
955     if (!idExist) {
956         entryIdMap_->InsertId(addr, sequenceId);
957     }
958     entryMap_.InsertEntry(node);
959     InsertNodeUnique(node);
960     return node;
961 }
962 
GeneratePrivateStringNode(size_t size)963 Node *HeapSnapshot::GeneratePrivateStringNode(size_t size)
964 {
965     if (privateStringNode_ != nullptr) {
966         return privateStringNode_;
967     }
968     JSTaggedValue stringValue = vm_->GetJSThread()->GlobalConstants()->GetStringString();
969     size_t selfsize = (size != 0) ? size :
970         stringValue.GetTaggedObject()->GetClass()->SizeFromJSHClass(stringValue.GetTaggedObject());
971     CString strContent;
972     strContent.append(EntryVisitor::ConvertKey(stringValue));
973     JSTaggedType addr = stringValue.GetRawData();
974     auto [idExist, sequenceId] = entryIdMap_->FindId(addr);
975     Node *node = Node::NewNode(chunk_, sequenceId, nodeCount_, GetString(strContent), NodeType::STRING, selfsize,
976                                0, addr);
977     if (!idExist) {
978         entryIdMap_->InsertId(addr, sequenceId);
979     }
980     entryMap_.InsertEntry(node);
981     InsertNodeUnique(node);
982     ASSERT(entryMap_.FindEntry(node->GetAddress())->GetAddress() == node->GetAddress());
983     privateStringNode_ = node;
984     return node;
985 }
986 
GenerateFunctionNode(JSTaggedValue entry,size_t size,bool isInFinish)987 Node *HeapSnapshot::GenerateFunctionNode(JSTaggedValue entry, size_t size, bool isInFinish)
988 {
989     TaggedObject *obj = entry.GetTaggedObject();
990     JSTaggedType addr = entry.GetRawData();
991     Node *existNode = entryMap_.FindEntry(addr);
992     auto [idExist, sequenceId] = entryIdMap_->FindId(addr);
993     if (existNode != nullptr) {
994         if (isInFinish) {
995             existNode->SetName(GetString(ParseFunctionName(obj)));
996         }
997         existNode->SetLive(true);
998         return existNode;
999     }
1000     size_t selfsize = (size != 0) ? size : obj->GetClass()->SizeFromJSHClass(obj);
1001     Node *node = Node::NewNode(chunk_, sequenceId, nodeCount_, GetString("JSFunction"), NodeType::CLOSURE, selfsize,
1002                                0, addr);
1003     if (isInFinish) {
1004         node->SetName(GetString(ParseFunctionName(obj)));
1005     }
1006     if (!idExist) {
1007         entryIdMap_->InsertId(addr, sequenceId);
1008     }
1009     entryMap_.InsertEntry(node);
1010     InsertNodeUnique(node);
1011     return node;
1012 }
1013 
GenerateObjectNode(JSTaggedValue entry,size_t size,bool isInFinish)1014 Node *HeapSnapshot::GenerateObjectNode(JSTaggedValue entry, size_t size, bool isInFinish)
1015 {
1016     TaggedObject *obj = entry.GetTaggedObject();
1017     JSTaggedType addr = entry.GetRawData();
1018     Node *existNode = entryMap_.FindEntry(addr);
1019     auto [idExist, sequenceId] = entryIdMap_->FindId(addr);
1020     if (existNode != nullptr) {
1021         if (isInFinish) {
1022             existNode->SetName(GetString(ParseObjectName(obj)));
1023         }
1024         existNode->SetLive(true);
1025         return existNode;
1026     }
1027     size_t selfsize = (size != 0) ? size : obj->GetClass()->SizeFromJSHClass(obj);
1028     Node *node = Node::NewNode(chunk_, sequenceId, nodeCount_, GetString("Object"), NodeType::OBJECT, selfsize,
1029                                0, addr);
1030     if (isInFinish) {
1031         node->SetName(GetString(ParseObjectName(obj)));
1032     }
1033     if (!idExist) {
1034         entryIdMap_->InsertId(addr, sequenceId);
1035     }
1036     entryMap_.InsertEntry(node);
1037     InsertNodeUnique(node);
1038     return node;
1039 }
1040 
FillEdges(bool isSimplify)1041 void HeapSnapshot::FillEdges(bool isSimplify)
1042 {
1043     LOG_ECMA(INFO) << "HeapSnapshot::FillEdges begin, nodeCount: " << nodeCount_;
1044     ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "HeapSnapshot::FillEdges");
1045     auto iter = nodes_.begin();
1046     size_t count = 0;
1047     while (count++ < nodes_.size()) {
1048         ASSERT(*iter != nullptr);
1049         auto entryFrom = *iter;
1050         JSTaggedValue value(entryFrom->GetAddress());
1051         if (!value.IsHeapObject()) {
1052             iter++;
1053             continue;
1054         }
1055         std::vector<Reference> referenceResources;
1056         value.DumpForSnapshot(referenceResources, isVmMode_);
1057         for (auto const &it : referenceResources) {
1058             JSTaggedValue toValue = it.value_;
1059             if (toValue.IsNumber() && !captureNumericValue_) {
1060                 continue;
1061             }
1062             Node *entryTo = nullptr;
1063             EdgeType type = toValue.IsWeak() ? EdgeType::WEAK : (EdgeType)it.type_;
1064             if (toValue.IsWeak()) {
1065                 toValue.RemoveWeakTag();
1066             }
1067             if (toValue.IsHeapObject()) {
1068                 auto *to = toValue.GetTaggedObject();
1069                 entryTo = entryMap_.FindEntry(Node::NewAddress(to));
1070             }
1071             if (entryTo == nullptr) {
1072                 entryTo = GenerateNode(toValue, 0, true, isSimplify);
1073             }
1074             if (entryTo != nullptr) {
1075                 Edge *edge = (it.type_ == Reference::ReferenceType::ELEMENT) ?
1076                     Edge::NewEdge(chunk_, type, entryFrom, entryTo, it.index_) :
1077                     Edge::NewEdge(chunk_, type, entryFrom, entryTo, GetString(it.name_));
1078                 RenameFunction(it.name_, entryFrom, entryTo);
1079                 InsertEdgeUnique(edge);
1080                 (*iter)->IncEdgeCount();  // Update Node's edgeCount_ here
1081             }
1082         }
1083         iter++;
1084     }
1085     LOG_ECMA(INFO) << "HeapSnapshot::FillEdges exit, nodeCount: " << nodeCount_ << ", edgeCount: " << edgeCount_;
1086 }
1087 
FillEdgesForBinMod(char * newAddr,CUnorderedSet<uint64_t> * refSet)1088 void HeapSnapshot::FillEdgesForBinMod(char *newAddr, CUnorderedSet<uint64_t> *refSet)
1089 {
1090     auto entryFrom = entryMap_.FindEntry(reinterpret_cast<JSTaggedType>(newAddr));
1091     JSTaggedValue value(entryFrom->GetAddress());
1092     auto object = value.GetTaggedObject();
1093     std::vector<Reference> referenceResources;
1094     auto jsHclass = object->GetClass();
1095     if (jsHclass->IsJsGlobalEnv() || jsHclass->IsString()) {
1096         referenceResources.emplace_back("hclass", JSTaggedValue(jsHclass));
1097         if (refSet != nullptr) {
1098             for (auto refAddr : *refSet) {
1099                 JSTaggedValue val(refAddr);
1100                 auto valTy = val.GetTaggedObject()->GetClass()->GetObjectType();
1101                 referenceResources.emplace_back(JSHClass::DumpJSType(valTy), val);
1102             }
1103         }
1104     } else {
1105         value.DumpForSnapshot(referenceResources, false);
1106     }
1107     for (auto const &it : referenceResources) {
1108         JSTaggedValue toValue = it.value_;
1109         if (toValue.IsNumber() && !captureNumericValue_) {
1110             continue;
1111         }
1112         Node *entryTo = nullptr;
1113         EdgeType type = toValue.IsWeak() ? EdgeType::WEAK : (EdgeType)it.type_;
1114         if (toValue.IsWeak()) {
1115             toValue.RemoveWeakTag();
1116         }
1117         if (toValue.IsHeapObject()) {
1118             auto *to = toValue.GetTaggedObject();
1119             entryTo = entryMap_.FindEntry(Node::NewAddress(to));
1120         }
1121         if (entryTo == nullptr) {
1122             CString name;
1123             name.append("MissObj").append(std::to_string(toValue.GetRawData()));
1124             Node *missObj = Node::NewNode(chunk_, 1, nodeCount_, GetString(name), NodeType::OBJECT, 0, 0, 0);
1125             entryMap_.InsertEntry(missObj);
1126             InsertNodeUnique(missObj);
1127         }
1128         if (entryTo != nullptr) {
1129             Edge *edge = (it.type_ == Reference::ReferenceType::ELEMENT) ?
1130                 Edge::NewEdge(chunk_, type, entryFrom, entryTo, it.index_) :
1131                 Edge::NewEdge(chunk_, type, entryFrom, entryTo, GetString(it.name_));
1132             RenameFunction(it.name_, entryFrom, entryTo);
1133             InsertEdgeUnique(edge);
1134             entryFrom->IncEdgeCount();  // Update Node's edgeCount_ here
1135         }
1136     }
1137 }
1138 
GenerateNodeForBinMod(CUnorderedMap<uint64_t,NewAddr * > & objMap,CUnorderedSet<uint64_t> & rootSet,CUnorderedMap<uint64_t,CString * > & strTableIdMap)1139 void HeapSnapshot::GenerateNodeForBinMod(CUnorderedMap<uint64_t, NewAddr *> &objMap, CUnorderedSet<uint64_t> &rootSet,
1140                                          CUnorderedMap<uint64_t, CString *> &strTableIdMap)
1141 {
1142     Node *syntheticRoot = Node::NewNode(chunk_, 1, nodeCount_, GetString("SyntheticRoot"),
1143                                         NodeType::SYNTHETIC, 0, 0, 0);
1144     InsertNodeAt(0, syntheticRoot);
1145     CList<Edge *> rootEdges;
1146     for (auto objItem : objMap) {
1147         TaggedObject *obj = reinterpret_cast<TaggedObject *>(objItem.second->Data());
1148         auto currNode = GenerateNode(JSTaggedValue(obj), objItem.second->objSize, false, false, false);
1149         if (currNode == nullptr) {
1150             continue;
1151         }
1152         if (strTableIdMap.find(objItem.first) != strTableIdMap.end()) {
1153             currNode->SetName(strTableIdMap[objItem.first]);
1154         }
1155         if (rootSet.find(objItem.first) != rootSet.end()) {
1156             Edge *edge = Edge::NewEdge(chunk_, EdgeType::SHORTCUT, syntheticRoot, currNode, GetString("-subroot-"));
1157             rootEdges.emplace_back(edge);
1158             syntheticRoot->IncEdgeCount();
1159         }
1160     }
1161     // add root edges to edges begin
1162     edges_.insert(edges_.begin(), rootEdges.begin(), rootEdges.end());
1163     edgeCount_ += rootEdges.size();
1164 }
1165 
BuildSnapshotForBinMod(CUnorderedMap<uint64_t,NewAddr * > & objMap,CUnorderedMap<uint64_t,CUnorderedSet<uint64_t>> & refSetMap)1166 bool HeapSnapshot::BuildSnapshotForBinMod(CUnorderedMap<uint64_t, NewAddr *> &objMap,
1167                                           CUnorderedMap<uint64_t, CUnorderedSet<uint64_t>> &refSetMap)
1168 {
1169     for (auto objItem : objMap) {
1170         CUnorderedSet<uint64_t> *refSet = nullptr;
1171         auto newAddr = reinterpret_cast<uint64_t>(objItem.second->Data());
1172         if (refSetMap.find(newAddr) != refSetMap.end()) {
1173             refSet = &refSetMap[newAddr];
1174         }
1175         FillEdgesForBinMod(objItem.second->Data(), refSet);
1176     }
1177     int reindex = 0;
1178     for (Node *node : nodes_) {
1179         node->SetIndex(reindex);
1180         reindex++;
1181     }
1182     return Verify();
1183 }
1184 
RenameFunction(const CString & edgeName,Node * entryFrom,Node * entryTo)1185 void HeapSnapshot::RenameFunction(const CString &edgeName, Node *entryFrom, Node *entryTo)
1186 {
1187     if (edgeName != "name") {
1188         return;
1189     }
1190     if (entryFrom->GetType() != NodeType::CLOSURE) {
1191         return;
1192     }
1193     if (*entryFrom->GetName() == "JSFunction" && *entryTo->GetName() != "" &&
1194         *entryTo->GetName() != "InternalAccessor") {
1195         entryFrom->SetName(GetString(*entryTo->GetName()));
1196     }
1197 }
1198 
ParseFunctionName(TaggedObject * obj,bool isRawHeap)1199 CString HeapSnapshot::ParseFunctionName(TaggedObject *obj, bool isRawHeap)
1200 {
1201     CString result;
1202     JSFunctionBase *func = JSFunctionBase::Cast(obj);
1203     Method *method = Method::Cast(func->GetMethod().GetTaggedObject());
1204     MethodLiteral *methodLiteral = method->GetMethodLiteral();
1205     if (methodLiteral == nullptr) {
1206         if (!isRawHeap) {
1207             return "JSFunction";
1208         }
1209         JSHandle<JSFunctionBase> funcBase = JSHandle<JSFunctionBase>(vm_->GetJSThread(), obj);
1210         auto funcName = JSFunction::GetFunctionName(vm_->GetJSThread(), funcBase);
1211         if (funcName->IsString()) {
1212             auto name = EcmaStringAccessor(JSHandle<EcmaString>::Cast(funcName)).ToCString();
1213             return name.empty() ? "JSFunction" : name;
1214         }
1215         return "JSFunction";
1216     }
1217     const JSPandaFile *jsPandaFile = method->GetJSPandaFile();
1218     panda_file::File::EntityId methodId = methodLiteral->GetMethodId();
1219     const CString &nameStr = MethodLiteral::ParseFunctionNameToCString(jsPandaFile, methodId);
1220     const CString &moduleStr = method->GetRecordNameStr();
1221 
1222     if (!moduleStr.empty()) {
1223         result.append(moduleStr).append(" ");
1224     }
1225     if (nameStr.empty()) {
1226         result.append("anonymous");
1227     } else {
1228         result.append(nameStr);
1229     }
1230     DebugInfoExtractor *debugExtractor =
1231         JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile);
1232     if (debugExtractor == nullptr) {
1233         return result;
1234     }
1235     int32_t line = debugExtractor->GetFristLine(methodId);
1236     return result.append("(line:").append(std::to_string(line)).append(")");
1237 }
1238 
ParseObjectName(TaggedObject * obj)1239 const CString HeapSnapshot::ParseObjectName(TaggedObject *obj)
1240 {
1241     ASSERT(JSTaggedValue(obj).IsJSObject());
1242     JSThread *thread = vm_->GetJSThread();
1243     bool isCallGetter = false;
1244     return JSObject::ExtractConstructorAndRecordName(thread, obj, true, &isCallGetter);
1245 }
1246 
InsertNodeUnique(Node * node)1247 Node *HeapSnapshot::InsertNodeUnique(Node *node)
1248 {
1249     AccumulateNodeSize(node->GetSelfSize());
1250     nodes_.emplace_back(node);
1251     nodeCount_++;
1252     return node;
1253 }
1254 
EraseNodeUnique(Node * node)1255 void HeapSnapshot::EraseNodeUnique(Node *node)
1256 {
1257     auto iter = std::find(nodes_.begin(), nodes_.end(), node);
1258     if (iter != nodes_.end()) {
1259         DecreaseNodeSize(node->GetSelfSize());
1260         chunk_.Delete(node);
1261         nodes_.erase(iter);
1262         nodeCount_--;
1263     }
1264 }
1265 
InsertEdgeUnique(Edge * edge)1266 Edge *HeapSnapshot::InsertEdgeUnique(Edge *edge)
1267 {
1268     edges_.emplace_back(edge);
1269     edgeCount_++;
1270     return edge;
1271 }
1272 
AddSyntheticRoot()1273 void HeapSnapshot::AddSyntheticRoot()
1274 {
1275     LOG_ECMA(INFO) << "HeapSnapshot::AddSyntheticRoot";
1276     ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "HeapSnapshot::AddSyntheticRoot");
1277     Node *syntheticRoot = Node::NewNode(chunk_, 1, nodeCount_, GetString("SyntheticRoot"),
1278                                         NodeType::SYNTHETIC, 0, 0, 0);
1279     InsertNodeAt(0, syntheticRoot);
1280     CUnorderedSet<JSTaggedType> values {};
1281     CList<Edge *> rootEdges;
1282 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
1283 #define ROOT_EDGE_BUILDER_CORE(type, slot)                                                            \
1284     do {                                                                                              \
1285         JSTaggedValue value((slot).GetTaggedType());                                                  \
1286         if (value.IsHeapObject()) {                                                                   \
1287             TaggedObject *root = value.GetTaggedObject();                                             \
1288             Node *rootNode = entryMap_.FindEntry(Node::NewAddress(root));                             \
1289             if (rootNode != nullptr) {                                                                \
1290                 JSTaggedType valueTo = value.GetRawData();                                            \
1291                 auto it = values.find(valueTo);                                                       \
1292                 if (it == values.end()) {                                                             \
1293                     values.insert(valueTo);                                                           \
1294                     Edge *edge = Edge::NewEdge(chunk_,                                                \
1295                         EdgeType::SHORTCUT, syntheticRoot, rootNode, GetString("-subroot-"));         \
1296                     rootEdges.emplace_back(edge);                                                     \
1297                     syntheticRoot->IncEdgeCount();                                                    \
1298                 }                                                                                     \
1299             }                                                                                         \
1300         }                                                                                             \
1301     } while (false)
1302 
1303     RootVisitor rootEdgeBuilder = [this, syntheticRoot, &rootEdges, &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, &rootEdges, &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     // add root edges to edges begin
1322     edges_.insert(edges_.begin(), rootEdges.begin(), rootEdges.end());
1323     edgeCount_ += rootEdges.size();
1324     int reindex = 0;
1325     for (Node *node : nodes_) {
1326         node->SetIndex(reindex);
1327         reindex++;
1328     }
1329 }
1330 
InsertNodeAt(size_t pos,Node * node)1331 Node *HeapSnapshot::InsertNodeAt(size_t pos, Node *node)
1332 {
1333     ASSERT(node != nullptr);
1334     auto iter = nodes_.begin();
1335     std::advance(iter, static_cast<int>(pos));
1336     nodes_.insert(iter, node);
1337     nodeCount_++;
1338     return node;
1339 }
1340 
InsertEdgeAt(size_t pos,Edge * edge)1341 Edge *HeapSnapshot::InsertEdgeAt(size_t pos, Edge *edge)
1342 {
1343     ASSERT(edge != nullptr);
1344     auto iter = edges_.begin();
1345     std::advance(iter, static_cast<int>(pos));
1346     edges_.insert(iter, edge);
1347     edgeCount_++;
1348     return edge;
1349 }
1350 
ConvertKey(JSTaggedValue key)1351 CString EntryVisitor::ConvertKey(JSTaggedValue key)
1352 {
1353     ASSERT(key.GetTaggedObject() != nullptr);
1354     EcmaString *keyString = EcmaString::Cast(key.GetTaggedObject());
1355 
1356     if (key.IsSymbol()) {
1357         JSSymbol *symbol = JSSymbol::Cast(key.GetTaggedObject());
1358         keyString = EcmaString::Cast(symbol->GetDescription().GetTaggedObject());
1359     }
1360     // convert, expensive but safe
1361     return EcmaStringAccessor(keyString).ToCString(StringConvertedUsage::PRINT);
1362 }
1363 
FindOrInsertNode(Node * node)1364 Node *HeapEntryMap::FindOrInsertNode(Node *node)
1365 {
1366     ASSERT(node != nullptr);
1367     auto it = nodesMap_.find(node->GetAddress());
1368     if (it != nodesMap_.end()) {
1369         return it->second;
1370     }
1371     InsertEntry(node);
1372     return node;
1373 }
1374 
FindAndEraseNode(JSTaggedType addr)1375 Node *HeapEntryMap::FindAndEraseNode(JSTaggedType addr)
1376 {
1377     auto it = nodesMap_.find(addr);
1378     if (it != nodesMap_.end()) {
1379         Node *node = it->second;
1380         nodesMap_.erase(it);
1381         return node;
1382     }
1383     return nullptr;
1384 }
1385 
FindEntry(JSTaggedType addr)1386 Node *HeapEntryMap::FindEntry(JSTaggedType addr)
1387 {
1388     auto it = nodesMap_.find(addr);
1389     return it != nodesMap_.end() ? it->second : nullptr;
1390 }
1391 
InsertEntry(Node * node)1392 void HeapEntryMap::InsertEntry(Node *node)
1393 {
1394     nodesMap_.emplace(node->GetAddress(), node);
1395 }
1396 }  // namespace panda::ecmascript
1397