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