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