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