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