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