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/dfx/hprof/heap_root_visitor.h"
21 #include "ecmascript/ecma_string-inl.h"
22 #include "ecmascript/global_dictionary.h"
23 #include "ecmascript/global_env.h"
24 #include "ecmascript/ic/property_box.h"
25 #include "ecmascript/js_array.h"
26 #include "ecmascript/js_handle.h"
27 #include "ecmascript/js_hclass-inl.h"
28 #include "ecmascript/js_object-inl.h"
29 #include "ecmascript/js_symbol.h"
30 #include "ecmascript/js_tagged_value-inl.h"
31 #include "ecmascript/js_thread.h"
32 #include "ecmascript/jspandafile/program_object.h"
33 #include "ecmascript/mem/assert_scope.h"
34 #include "ecmascript/property_attributes.h"
35 #include "ecmascript/tagged_array.h"
36 #include "ecmascript/tagged_dictionary.h"
37 #include "ecmascript/jspandafile/js_pandafile_manager.h"
38
39 namespace panda::ecmascript {
GetString(const CString & as)40 CString *HeapSnapshot::GetString(const CString &as)
41 {
42 return stringTable_.GetString(as);
43 }
44
GetArrayString(TaggedArray * array,const CString & as)45 CString *HeapSnapshot::GetArrayString(TaggedArray *array, const CString &as)
46 {
47 CString arrayName = as;
48 arrayName.append(ToCString(array->GetLength()));
49 arrayName.append("]");
50 return GetString(arrayName); // String type was handled singly, see#GenerateStringNode
51 }
52
NewNode(Chunk * chunk,size_t id,size_t index,const CString * name,NodeType type,size_t size,TaggedObject * entry,bool isLive)53 Node *Node::NewNode(Chunk *chunk, size_t id, size_t index, const CString *name, NodeType type, size_t size,
54 TaggedObject *entry, bool isLive)
55 {
56 auto node = chunk->New<Node>(id, index, name, type, size, 0, NewAddress<TaggedObject>(entry), isLive);
57 if (UNLIKELY(node == nullptr)) {
58 LOG_FULL(FATAL) << "internal allocator failed";
59 UNREACHABLE();
60 }
61 return node;
62 }
63
NewEdge(Chunk * chunk,uint32_t id,EdgeType type,Node * from,Node * to,CString * name)64 Edge *Edge::NewEdge(Chunk *chunk, uint32_t id, EdgeType type, Node *from, Node *to, CString *name)
65 {
66 auto edge = chunk->New<Edge>(id, type, from, to, name);
67 if (UNLIKELY(edge == nullptr)) {
68 LOG_FULL(FATAL) << "internal allocator failed";
69 UNREACHABLE();
70 }
71 return edge;
72 }
73
~HeapSnapshot()74 HeapSnapshot::~HeapSnapshot()
75 {
76 for (Node *node : nodes_) {
77 chunk_->Delete(node);
78 }
79 for (Edge *edge : edges_) {
80 chunk_->Delete(edge);
81 }
82 nodes_.clear();
83 edges_.clear();
84 traceInfoStack_.clear();
85 stackInfo_.clear();
86 scriptIdMap_.clear();
87 methodToTraceNodeId_.clear();
88 traceNodeIndex_.clear();
89 entryIdMap_ = nullptr;
90 chunk_ = nullptr;
91 }
92
BuildUp()93 bool HeapSnapshot::BuildUp()
94 {
95 FillNodes(true);
96 FillEdges();
97 AddSyntheticRoot();
98 return Verify();
99 }
100
Verify()101 bool HeapSnapshot::Verify()
102 {
103 GetString(CString("HeapVerify:").append(ToCString(totalNodesSize_)));
104 return (edgeCount_ > nodeCount_) && (totalNodesSize_ > 0);
105 }
106
PrepareSnapshot()107 void HeapSnapshot::PrepareSnapshot()
108 {
109 FillNodes();
110 if (trackAllocations()) {
111 PrepareTraceInfo();
112 }
113 }
114
UpdateNodes(bool isInFinish)115 void HeapSnapshot::UpdateNodes(bool isInFinish)
116 {
117 for (Node *node : nodes_) {
118 node->SetLive(false);
119 }
120 FillNodes(isInFinish);
121 for (auto iter = nodes_.begin(); iter != nodes_.end();) {
122 if (!(*iter)->IsLive()) {
123 Node *node = entryMap_.FindAndEraseNode((*iter)->GetAddress());
124 ASSERT(*iter == node);
125 entryIdMap_->EraseId((*iter)->GetAddress());
126 if (node != nullptr) {
127 DecreaseNodeSize(node->GetSelfSize());
128 chunk_->Delete(node);
129 }
130 iter = nodes_.erase(iter);
131 nodeCount_--;
132 } else {
133 iter++;
134 }
135 }
136 }
137
FinishSnapshot()138 bool HeapSnapshot::FinishSnapshot()
139 {
140 UpdateNodes(true);
141 FillEdges();
142 AddSyntheticRoot();
143 return Verify();
144 }
145
RecordSampleTime()146 void HeapSnapshot::RecordSampleTime()
147 {
148 timeStamps_.emplace_back(entryIdMap_->GetLastId());
149 }
150
PushHeapStat(Stream * stream)151 void HeapSnapshot::PushHeapStat(Stream* stream)
152 {
153 CVector<HeapStat> statsBuffer;
154 if (stream == nullptr) {
155 LOG_DEBUGGER(ERROR) << "HeapSnapshot::PushHeapStat::stream is nullptr";
156 return;
157 }
158 int32_t preChunkSize = stream->GetSize();
159 int32_t sequenceId = 0;
160 int64_t timeStampUs = 0;
161 auto iter = nodes_.begin();
162 for (size_t timeIndex = 0; timeIndex < timeStamps_.size(); ++timeIndex) {
163 TimeStamp& timeStamp = timeStamps_[timeIndex];
164 sequenceId = timeStamp.GetLastSequenceId();
165 timeStampUs = timeStamp.GetTimeStamp();
166 uint32_t nodesSize = 0;
167 uint32_t nodesCount = 0;
168 while (iter != nodes_.end() && (*iter)->GetId() <= static_cast<uint32_t>(sequenceId)) {
169 nodesCount++;
170 nodesSize += (*iter)->GetSelfSize();
171 iter++;
172 }
173 if ((timeStamp.GetCount() != nodesCount) || (timeStamp.GetSize() != nodesSize)) {
174 timeStamp.SetCount(nodesCount);
175 timeStamp.SetSize(nodesSize);
176 statsBuffer.emplace_back(static_cast<int32_t>(timeIndex), nodesCount, nodesSize);
177 if (static_cast<int32_t>(statsBuffer.size()) >= preChunkSize) {
178 stream->UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size()));
179 statsBuffer.clear();
180 }
181 }
182 }
183 if (!statsBuffer.empty()) {
184 stream->UpdateHeapStats(&statsBuffer.front(), static_cast<int32_t>(statsBuffer.size()));
185 statsBuffer.clear();
186 }
187 stream->UpdateLastSeenObjectId(sequenceId, timeStampUs);
188 }
189
AddNode(TaggedObject * address,size_t size)190 Node *HeapSnapshot::AddNode(TaggedObject *address, size_t size)
191 {
192 return GenerateNode(JSTaggedValue(address), size);
193 }
194
MoveNode(uintptr_t address,TaggedObject * forwardAddress,size_t size)195 void HeapSnapshot::MoveNode(uintptr_t address, TaggedObject *forwardAddress, size_t size)
196 {
197 if (address == reinterpret_cast<uintptr_t>(forwardAddress)) {
198 return;
199 }
200
201 Node *node = entryMap_.FindAndEraseNode(address);
202 if (node != nullptr) {
203 ASSERT(node->GetId() <= UINT_MAX);
204
205 Node *oldNode = entryMap_.FindAndEraseNode(Node::NewAddress(forwardAddress));
206 if (oldNode != nullptr) {
207 EraseNodeUnique(oldNode);
208 }
209
210 // Size and name may change during its life for some types(such as string, array and etc).
211 if (forwardAddress->GetClass() != nullptr) {
212 node->SetName(GenerateNodeName(forwardAddress));
213 }
214 if (JSTaggedValue(forwardAddress).IsString()) {
215 node->SetSelfSize(EcmaStringAccessor(forwardAddress).GetFlatStringSize());
216 } else {
217 node->SetSelfSize(size);
218 }
219 node->SetAddress(Node::NewAddress(forwardAddress));
220 entryMap_.InsertEntry(node);
221 } else {
222 LOG_DEBUGGER(WARN) << "Untracked object moves from " << address << " to " << forwardAddress;
223 int32_t sequenceId = -1;
224 GenerateNode(JSTaggedValue(forwardAddress), size, sequenceId);
225 }
226 }
227
228 // NOLINTNEXTLINE(readability-function-size)
GenerateNodeName(TaggedObject * entry)229 CString *HeapSnapshot::GenerateNodeName(TaggedObject *entry)
230 {
231 auto *hCls = entry->GetClass();
232 JSType type = hCls->GetObjectType();
233 switch (type) {
234 case JSType::TAGGED_ARRAY:
235 return GetArrayString(TaggedArray::Cast(entry), "TaggedArray[");
236 case JSType::LEXICAL_ENV:
237 return GetArrayString(TaggedArray::Cast(entry), "LexicalEnv[");
238 case JSType::CONSTANT_POOL:
239 return GetArrayString(TaggedArray::Cast(entry), "ConstantPool[");
240 case JSType::TAGGED_DICTIONARY:
241 return GetArrayString(TaggedArray::Cast(entry), "TaggedDict[");
242 case JSType::AOT_LITERAL_INFO:
243 return GetArrayString(TaggedArray::Cast(entry), "AOTLiteralInfo[");
244 case JSType::VTABLE:
245 return GetArrayString(TaggedArray::Cast(entry), "VTable[");
246 case JSType::COW_TAGGED_ARRAY:
247 return GetArrayString(TaggedArray::Cast(entry), "COWArray[");
248 case JSType::HCLASS:
249 return GetString("HiddenClass");
250 case JSType::LINE_STRING:
251 case JSType::CONSTANT_STRING:
252 case JSType::TREE_STRING:
253 return GetString("BaseString");
254 case JSType::JS_OBJECT: {
255 CString objName = CString("JSOBJECT(Ctor="); // Ctor-name
256 return GetString(objName);
257 }
258 case JSType::FREE_OBJECT_WITH_ONE_FIELD:
259 case JSType::FREE_OBJECT_WITH_NONE_FIELD:
260 case JSType::FREE_OBJECT_WITH_TWO_FIELD:
261 return GetString("FreeObject");
262 case JSType::JS_NATIVE_POINTER:
263 return GetString("JSNativePointer");
264 case JSType::JS_FUNCTION_BASE:
265 return GetString("JSFunctionBase");
266 case JSType::JS_FUNCTION:
267 return GetString("JSFunction");
268 case JSType::JS_ERROR:
269 return GetString("Error");
270 case JSType::JS_EVAL_ERROR:
271 return GetString("Eval Error");
272 case JSType::JS_RANGE_ERROR:
273 return GetString("Range Error");
274 case JSType::JS_TYPE_ERROR:
275 return GetString("Type Error");
276 case JSType::JS_AGGREGATE_ERROR:
277 return GetString("Aggregate Error");
278 case JSType::JS_REFERENCE_ERROR:
279 return GetString("Reference Error");
280 case JSType::JS_URI_ERROR:
281 return GetString("Uri Error");
282 case JSType::JS_SYNTAX_ERROR:
283 return GetString("Syntax Error");
284 case JSType::JS_OOM_ERROR:
285 return GetString("OutOfMemory Error");
286 case JSType::JS_REG_EXP:
287 return GetString("Regexp");
288 case JSType::JS_SET:
289 return GetString("Set");
290 case JSType::JS_MAP:
291 return GetString("Map");
292 case JSType::JS_WEAK_SET:
293 return GetString("WeakSet");
294 case JSType::JS_WEAK_MAP:
295 return GetString("WeakMap");
296 case JSType::JS_DATE:
297 return GetString("Date");
298 case JSType::JS_BOUND_FUNCTION:
299 return GetString("Bound Function");
300 case JSType::JS_ARRAY: {
301 JSArray *jsArray = JSArray::Cast(entry);
302 CString jsArrayName("JSArray[");
303 jsArrayName.append(ToCString(jsArray->GetLength()));
304 jsArrayName.append("]");
305 return GetString(jsArrayName);
306 }
307 case JSType::JS_TYPED_ARRAY:
308 return GetString("Typed Array");
309 case JSType::JS_INT8_ARRAY:
310 return GetString("Int8 Array");
311 case JSType::JS_UINT8_ARRAY:
312 return GetString("Uint8 Array");
313 case JSType::JS_UINT8_CLAMPED_ARRAY:
314 return GetString("Uint8 Clamped Array");
315 case JSType::JS_INT16_ARRAY:
316 return GetString("Int16 Array");
317 case JSType::JS_UINT16_ARRAY:
318 return GetString("Uint16 Array");
319 case JSType::JS_INT32_ARRAY:
320 return GetString("Int32 Array");
321 case JSType::JS_UINT32_ARRAY:
322 return GetString("Uint32 Array");
323 case JSType::JS_FLOAT32_ARRAY:
324 return GetString("Float32 Array");
325 case JSType::JS_FLOAT64_ARRAY:
326 return GetString("Float64 Array");
327 case JSType::JS_BIGINT64_ARRAY:
328 return GetString("BigInt64 Array");
329 case JSType::JS_BIGUINT64_ARRAY:
330 return GetString("BigUint64 Array");
331 case JSType::JS_ARGUMENTS:
332 return GetString("Arguments");
333 case JSType::BIGINT:
334 return GetString("BigInt");
335 case JSType::JS_PROXY:
336 return GetString("Proxy");
337 case JSType::JS_PRIMITIVE_REF:
338 return GetString("Primitive");
339 case JSType::JS_DATA_VIEW:
340 return GetString("DataView");
341 case JSType::JS_ITERATOR:
342 return GetString("Iterator");
343 case JSType::JS_FORIN_ITERATOR:
344 return GetString("ForinInterator");
345 case JSType::JS_MAP_ITERATOR:
346 return GetString("MapIterator");
347 case JSType::JS_SET_ITERATOR:
348 return GetString("SetIterator");
349 case JSType::JS_REG_EXP_ITERATOR:
350 return GetString("RegExpIterator");
351 case JSType::JS_ARRAY_ITERATOR:
352 return GetString("ArrayIterator");
353 case JSType::JS_STRING_ITERATOR:
354 return GetString("StringIterator");
355 case JSType::JS_ARRAY_BUFFER:
356 return GetString("ArrayBuffer");
357 case JSType::JS_SHARED_ARRAY_BUFFER:
358 return GetString("SharedArrayBuffer");
359 case JSType::JS_PROXY_REVOC_FUNCTION:
360 return GetString("ProxyRevocFunction");
361 case JSType::PROMISE_REACTIONS:
362 return GetString("PromiseReaction");
363 case JSType::PROMISE_CAPABILITY:
364 return GetString("PromiseCapability");
365 case JSType::ASYNC_GENERATOR_REQUEST:
366 return GetString("AsyncGeneratorRequest");
367 case JSType::PROMISE_ITERATOR_RECORD:
368 return GetString("PromiseIteratorRecord");
369 case JSType::PROMISE_RECORD:
370 return GetString("PromiseRecord");
371 case JSType::RESOLVING_FUNCTIONS_RECORD:
372 return GetString("ResolvingFunctionsRecord");
373 case JSType::JS_PROMISE:
374 return GetString("Promise");
375 case JSType::JS_PROMISE_REACTIONS_FUNCTION:
376 return GetString("PromiseReactionsFunction");
377 case JSType::JS_PROMISE_EXECUTOR_FUNCTION:
378 return GetString("PromiseExecutorFunction");
379 case JSType::JS_ASYNC_FROM_SYNC_ITER_UNWARP_FUNCTION:
380 return GetString("AsyncFromSyncIterUnwarpFunction");
381 case JSType::JS_PROMISE_ALL_RESOLVE_ELEMENT_FUNCTION:
382 return GetString("PromiseAllResolveElementFunction");
383 case JSType::JS_PROMISE_ANY_REJECT_ELEMENT_FUNCTION:
384 return GetString("PromiseAnyRejectElementFunction");
385 case JSType::JS_PROMISE_ALL_SETTLED_ELEMENT_FUNCTION:
386 return GetString("PromiseAllSettledElementFunction");
387 case JSType::JS_PROMISE_FINALLY_FUNCTION:
388 return GetString("PromiseFinallyFunction");
389 case JSType::JS_PROMISE_VALUE_THUNK_OR_THROWER_FUNCTION:
390 return GetString("PromiseValueThunkOrThrowerFunction");
391 case JSType::JS_ASYNC_GENERATOR_RESUME_NEXT_RETURN_PROCESSOR_RST_FTN:
392 return GetString("AsyncGeneratorResumeNextReturnProcessorRstFtn");
393 case JSType::JS_GENERATOR_FUNCTION:
394 return GetString("JSGeneratorFunction");
395 case JSType::JS_ASYNC_GENERATOR_FUNCTION:
396 return GetString("JSAsyncGeneratorFunction");
397 case JSType::SYMBOL:
398 return GetString("Symbol");
399 case JSType::JS_ASYNC_FUNCTION:
400 return GetString("AsyncFunction");
401 case JSType::JS_INTL_BOUND_FUNCTION:
402 return GetString("JSIntlBoundFunction");
403 case JSType::JS_ASYNC_AWAIT_STATUS_FUNCTION:
404 return GetString("AsyncAwaitStatusFunction");
405 case JSType::JS_ASYNC_FUNC_OBJECT:
406 return GetString("AsyncFunctionObject");
407 case JSType::JS_REALM:
408 return GetString("Realm");
409 case JSType::JS_GLOBAL_OBJECT:
410 return GetString("GlobalObject");
411 case JSType::JS_INTL:
412 return GetString("JSIntl");
413 case JSType::JS_LOCALE:
414 return GetString("JSLocale");
415 case JSType::JS_DATE_TIME_FORMAT:
416 return GetString("JSDateTimeFormat");
417 case JSType::JS_RELATIVE_TIME_FORMAT:
418 return GetString("JSRelativeTimeFormat");
419 case JSType::JS_NUMBER_FORMAT:
420 return GetString("JSNumberFormat");
421 case JSType::JS_COLLATOR:
422 return GetString("JSCollator");
423 case JSType::JS_PLURAL_RULES:
424 return GetString("JSPluralRules");
425 case JSType::JS_DISPLAYNAMES:
426 return GetString("JSDisplayNames");
427 case JSType::JS_LIST_FORMAT:
428 return GetString("JSListFormat");
429 case JSType::JS_GENERATOR_OBJECT:
430 return GetString("JSGeneratorObject");
431 case JSType::JS_ASYNC_GENERATOR_OBJECT:
432 return GetString("JSAsyncGeneratorObject");
433 case JSType::JS_GENERATOR_CONTEXT:
434 return GetString("JSGeneratorContext");
435 case JSType::ACCESSOR_DATA:
436 return GetString("AccessorData");
437 case JSType::INTERNAL_ACCESSOR:
438 return GetString("InternalAccessor");
439 case JSType::MICRO_JOB_QUEUE:
440 return GetString("MicroJobQueue");
441 case JSType::PENDING_JOB:
442 return GetString("PendingJob");
443 case JSType::COMPLETION_RECORD:
444 return GetString("CompletionRecord");
445 case JSType::JS_API_ARRAY_LIST:
446 return GetString("ArrayList");
447 case JSType::JS_API_ARRAYLIST_ITERATOR:
448 return GetString("ArrayListIterator");
449 case JSType::JS_API_HASH_MAP:
450 return GetString("HashMap");
451 case JSType::JS_API_HASH_SET:
452 return GetString("HashSet");
453 case JSType::JS_API_HASHMAP_ITERATOR:
454 return GetString("HashMapIterator");
455 case JSType::JS_API_HASHSET_ITERATOR:
456 return GetString("HashSetIterator");
457 case JSType::JS_API_LIGHT_WEIGHT_MAP:
458 return GetString("LightWeightMap");
459 case JSType::JS_API_LIGHT_WEIGHT_MAP_ITERATOR:
460 return GetString("LightWeightMapIterator");
461 case JSType::JS_API_LIGHT_WEIGHT_SET:
462 return GetString("LightWeightSet");
463 case JSType::JS_API_LIGHT_WEIGHT_SET_ITERATOR:
464 return GetString("LightWeightSetIterator");
465 case JSType::JS_API_TREE_MAP:
466 return GetString("TreeMap");
467 case JSType::JS_API_TREE_SET:
468 return GetString("TreeSet");
469 case JSType::JS_API_TREEMAP_ITERATOR:
470 return GetString("TreeMapIterator");
471 case JSType::JS_API_TREESET_ITERATOR:
472 return GetString("TreeSetIterator");
473 case JSType::JS_API_VECTOR:
474 return GetString("Vector");
475 case JSType::JS_API_VECTOR_ITERATOR:
476 return GetString("VectorIterator");
477 case JSType::JS_API_QUEUE:
478 return GetString("Queue");
479 case JSType::JS_API_QUEUE_ITERATOR:
480 return GetString("QueueIterator");
481 case JSType::JS_API_DEQUE:
482 return GetString("Deque");
483 case JSType::JS_API_DEQUE_ITERATOR:
484 return GetString("DequeIterator");
485 case JSType::JS_API_STACK:
486 return GetString("Stack");
487 case JSType::JS_API_STACK_ITERATOR:
488 return GetString("StackIterator");
489 case JSType::JS_API_LIST:
490 return GetString("List");
491 case JSType::JS_API_LINKED_LIST:
492 return GetString("LinkedList");
493 case JSType::SOURCE_TEXT_MODULE_RECORD:
494 return GetString("SourceTextModule");
495 case JSType::IMPORTENTRY_RECORD:
496 return GetString("ImportEntry");
497 case JSType::LOCAL_EXPORTENTRY_RECORD:
498 return GetString("LocalExportEntry");
499 case JSType::INDIRECT_EXPORTENTRY_RECORD:
500 return GetString("IndirectExportEntry");
501 case JSType::STAR_EXPORTENTRY_RECORD:
502 return GetString("StarExportEntry");
503 case JSType::RESOLVEDBINDING_RECORD:
504 return GetString("ResolvedBinding");
505 case JSType::RESOLVEDINDEXBINDING_RECORD:
506 return GetString("ResolvedIndexBinding");
507 case JSType::JS_MODULE_NAMESPACE:
508 return GetString("ModuleNamespace");
509 case JSType::JS_API_PLAIN_ARRAY:
510 return GetString("PlainArray");
511 case JSType::JS_API_PLAIN_ARRAY_ITERATOR:
512 return GetString("PlainArrayIterator");
513 case JSType::JS_CJS_EXPORTS:
514 return GetString("CJS Exports");
515 case JSType::JS_CJS_MODULE:
516 return GetString("CJS Module");
517 case JSType::JS_CJS_REQUIRE:
518 return GetString("CJS Require");
519 case JSType::METHOD:
520 return GetString("Method");
521 default:
522 break;
523 }
524 if (IsInVmMode()) {
525 switch (type) {
526 case JSType::PROPERTY_BOX:
527 return GetString("PropertyBox");
528 case JSType::GLOBAL_ENV:
529 return GetString("GlobalEnv");
530 case JSType::PROTOTYPE_HANDLER:
531 return GetString("ProtoTypeHandler");
532 case JSType::TRANSITION_HANDLER:
533 return GetString("TransitionHandler");
534 case JSType::TRANS_WITH_PROTO_HANDLER:
535 return GetString("TransWithProtoHandler");
536 case JSType::STORE_TS_HANDLER:
537 return GetString("StoreTSHandler");
538 case JSType::PROTO_CHANGE_MARKER:
539 return GetString("ProtoChangeMarker");
540 case JSType::PROTOTYPE_INFO:
541 return GetString("ProtoChangeDetails");
542 case JSType::TEMPLATE_MAP:
543 return GetString("TemplateMap");
544 case JSType::PROGRAM:
545 return GetString("Program");
546 case JSType::MACHINE_CODE_OBJECT:
547 return GetString("MachineCode");
548 case JSType::CLASS_INFO_EXTRACTOR:
549 return GetString("ClassInfoExtractor");
550 case JSType::TS_OBJECT_TYPE:
551 return GetString("TSObjectType");
552 case JSType::TS_INTERFACE_TYPE:
553 return GetString("TSInterfaceType");
554 case JSType::TS_CLASS_TYPE:
555 return GetString("TSClassType");
556 case JSType::TS_UNION_TYPE:
557 return GetString("TSUnionType");
558 case JSType::TS_CLASS_INSTANCE_TYPE:
559 return GetString("TSClassInstanceType");
560 case JSType::TS_FUNCTION_TYPE:
561 return GetString("TSFunctionType");
562 case JSType::TS_ARRAY_TYPE:
563 return GetString("TSArrayType");
564 default:
565 break;
566 }
567 } else {
568 return GetString("Hidden Object");
569 }
570 return GetString(CString("UnKnownType").append(std::to_string(static_cast<int>(type))));
571 }
572
GenerateNodeType(TaggedObject * entry)573 NodeType HeapSnapshot::GenerateNodeType(TaggedObject *entry)
574 {
575 NodeType nodeType = NodeType::INVALID;
576 auto *hCls = entry->GetClass();
577 if (hCls->IsTaggedArray()) {
578 nodeType = NodeType::JS_ARRAY;
579 } else if (hCls->IsHClass()) {
580 nodeType = NodeType::HCLASS;
581 } else {
582 nodeType = NodeType(hCls->GetObjectType());
583 }
584 return nodeType;
585 }
586
FillNodes(bool isInFinish)587 void HeapSnapshot::FillNodes(bool isInFinish)
588 {
589 // Iterate Heap Object
590 auto heap = vm_->GetHeap();
591 if (heap != nullptr) {
592 heap->IterateOverObjects([this, isInFinish](TaggedObject *obj) {
593 GenerateNode(JSTaggedValue(obj), 0, isInFinish);
594 });
595 }
596 }
597
GenerateNode(JSTaggedValue entry,size_t size,bool isInFinish)598 Node *HeapSnapshot::GenerateNode(JSTaggedValue entry, size_t size, bool isInFinish)
599 {
600 Node *node = nullptr;
601 if (entry.IsHeapObject()) {
602 if (entry.IsWeak()) {
603 entry.RemoveWeakTag();
604 }
605 if (entry.IsString()) {
606 if (isPrivate_) {
607 node = GeneratePrivateStringNode(size);
608 } else {
609 node = GenerateStringNode(entry, size, isInFinish);
610 }
611 if (node == nullptr) {
612 LOG_ECMA(DEBUG) << "string node nullptr";
613 }
614 return node;
615 }
616 TaggedObject *obj = entry.GetTaggedObject();
617 auto *baseClass = obj->GetClass();
618 if (baseClass != nullptr) {
619 Address addr = reinterpret_cast<Address>(obj);
620 Node *existNode = entryMap_.FindEntry(addr); // Fast Index
621 auto [idExist, sequenceId] = entryIdMap_->FindId(addr);
622 if (existNode == nullptr) {
623 size_t selfSize = (size != 0) ? size : obj->GetClass()->SizeFromJSHClass(obj);
624 node = Node::NewNode(chunk_, sequenceId, nodeCount_, GenerateNodeName(obj), GenerateNodeType(obj),
625 selfSize, obj);
626 entryMap_.InsertEntry(node);
627 if (!idExist) {
628 entryIdMap_->InsertId(addr, sequenceId);
629 }
630 InsertNodeUnique(node);
631 ASSERT(entryMap_.FindEntry(node->GetAddress())->GetAddress() == node->GetAddress());
632 } else {
633 existNode->SetLive(true);
634 return existNode;
635 }
636 }
637 } else {
638 CString primitiveName;
639 if (entry.IsInt()) {
640 if (!captureNumericValue_) {
641 return nullptr;
642 }
643 primitiveName.append("Int:");
644 if (!isPrivate_) {
645 primitiveName.append(ToCString(entry.GetInt()));
646 }
647 } else if (entry.IsDouble()) {
648 if (!captureNumericValue_) {
649 return nullptr;
650 }
651 primitiveName.append("Double:");
652 if (!isPrivate_) {
653 primitiveName.append(FloatToCString(entry.GetDouble()));
654 }
655 } else if (entry.IsHole()) {
656 primitiveName.append("Hole");
657 } else if (entry.IsNull()) {
658 primitiveName.append("Null");
659 } else if (entry.IsTrue()) {
660 primitiveName.append("Boolean:true");
661 } else if (entry.IsFalse()) {
662 primitiveName.append("Boolean:false");
663 } else if (entry.IsException()) {
664 primitiveName.append("Exception");
665 } else if (entry.IsUndefined()) {
666 primitiveName.append("Undefined");
667 } else {
668 primitiveName.append("Illegal_Primitive");
669 }
670
671 // A primitive value with tag will be regarded as a pointer
672 auto *obj = reinterpret_cast<TaggedObject *>(entry.GetRawData());
673 Node *existNode = entryMap_.FindEntry(reinterpret_cast<Address>(obj));
674 if (existNode != nullptr) {
675 existNode->SetLive(true);
676 return existNode;
677 }
678 Address addr = reinterpret_cast<Address>(obj);
679 auto [idExist, sequenceId] = entryIdMap_->FindId(addr);
680 node = Node::NewNode(chunk_, sequenceId, nodeCount_, GetString(primitiveName), NodeType::JS_PRIMITIVE_REF, 0,
681 obj);
682 entryMap_.InsertEntry(node); // Fast Index
683 if (!idExist) {
684 entryIdMap_->InsertId(addr, sequenceId);
685 }
686 InsertNodeUnique(node);
687 }
688 return node;
689 }
690
TraceNode(TraceTree * tree,uint32_t nodeIndex)691 TraceNode::TraceNode(TraceTree* tree, uint32_t nodeIndex)
692 : tree_(tree),
693 nodeIndex_(nodeIndex),
694 totalSize_(0),
695 totalCount_(0),
696 id_(tree->GetNextNodeId())
697 {
698 }
699
~TraceNode()700 TraceNode::~TraceNode()
701 {
702 for (TraceNode* node : children_) {
703 delete node;
704 }
705 children_.clear();
706 }
707
AddNodeToTree(CVector<uint32_t> traceNodeIndex)708 TraceNode* TraceTree::AddNodeToTree(CVector<uint32_t> traceNodeIndex)
709 {
710 uint32_t len = traceNodeIndex.size();
711 if (len == 0) {
712 return nullptr;
713 }
714
715 TraceNode* node = GetRoot();
716 for (int i = static_cast<int>(len) - 1; i >= 0; i--) {
717 node = node->FindOrAddChild(traceNodeIndex[i]);
718 }
719 return node;
720 }
721
FindOrAddChild(uint32_t nodeIndex)722 TraceNode* TraceNode::FindOrAddChild(uint32_t nodeIndex)
723 {
724 TraceNode* child = FindChild(nodeIndex);
725 if (child == nullptr) {
726 child = new TraceNode(tree_, nodeIndex);
727 children_.push_back(child);
728 }
729 return child;
730 }
731
FindChild(uint32_t nodeIndex)732 TraceNode* TraceNode::FindChild(uint32_t nodeIndex)
733 {
734 for (TraceNode* node : children_) {
735 if (node->GetNodeIndex() == nodeIndex) {
736 return node;
737 }
738 }
739 return nullptr;
740 }
741
AddTraceNodeId(MethodLiteral * methodLiteral)742 void HeapSnapshot::AddTraceNodeId(MethodLiteral *methodLiteral)
743 {
744 uint32_t traceNodeId = 0;
745 auto result = methodToTraceNodeId_.find(methodLiteral);
746 if (result == methodToTraceNodeId_.end()) {
747 traceNodeId = traceInfoStack_.size() - 1;
748 methodToTraceNodeId_.emplace(methodLiteral, traceNodeId);
749 } else {
750 traceNodeId = result->second;
751 }
752 traceNodeIndex_.emplace_back(traceNodeId);
753 }
754
AddTraceNode(int sequenceId,int size)755 int HeapSnapshot::AddTraceNode(int sequenceId, int size)
756 {
757 traceNodeIndex_.clear();
758 auto thread = vm_->GetJSThread();
759 JSTaggedType *current = const_cast<JSTaggedType *>(thread->GetCurrentFrame());
760 FrameIterator it(current, thread);
761 for (; !it.Done(); it.Advance<GCVisitedFlag::VISITED>()) {
762 if (!it.IsJSFrame()) {
763 continue;
764 }
765 auto method = it.CheckAndGetMethod();
766 if (method == nullptr || method->IsNativeWithCallField()) {
767 continue;
768 }
769 MethodLiteral *methodLiteral = method->GetMethodLiteral();
770 if (methodLiteral == nullptr) {
771 continue;
772 }
773 if (stackInfo_.count(methodLiteral) == 0) {
774 if (!AddMethodInfo(methodLiteral, method->GetJSPandaFile(), sequenceId)) {
775 continue;
776 }
777 }
778 AddTraceNodeId(methodLiteral);
779 }
780
781 TraceNode* topNode = traceTree_.AddNodeToTree(traceNodeIndex_);
782 if (topNode == nullptr) {
783 return -1;
784 }
785 ASSERT(topNode->GetTotalSize() <= static_cast<uint32_t>(INT_MAX));
786 int totalSize = static_cast<int>(topNode->GetTotalSize());
787 totalSize += size;
788 topNode->SetTotalSize(totalSize);
789 uint32_t totalCount = topNode->GetTotalCount();
790 topNode->SetTotalCount(++totalCount);
791 return topNode->GetId();
792 }
793
AddMethodInfo(MethodLiteral * methodLiteral,const JSPandaFile * jsPandaFile,int sequenceId)794 bool HeapSnapshot::AddMethodInfo(MethodLiteral *methodLiteral,
795 const JSPandaFile *jsPandaFile,
796 int sequenceId)
797 {
798 struct FunctionInfo codeEntry;
799 codeEntry.functionId = sequenceId;
800 panda_file::File::EntityId methodId = methodLiteral->GetMethodId();
801 const std::string &functionName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
802 if (functionName.empty()) {
803 codeEntry.functionName = "anonymous";
804 } else {
805 codeEntry.functionName = functionName;
806 }
807 GetString(codeEntry.functionName.c_str());
808
809 // source file
810 DebugInfoExtractor *debugExtractor =
811 JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile);
812 const std::string &sourceFile = debugExtractor->GetSourceFile(methodId);
813 if (sourceFile.empty()) {
814 codeEntry.scriptName = "";
815 } else {
816 codeEntry.scriptName = sourceFile;
817 auto iter = scriptIdMap_.find(codeEntry.scriptName);
818 if (iter == scriptIdMap_.end()) {
819 scriptIdMap_.emplace(codeEntry.scriptName, scriptIdMap_.size() + 1);
820 codeEntry.scriptId = static_cast<int>(scriptIdMap_.size());
821 } else {
822 codeEntry.scriptId = iter->second;
823 }
824 }
825 GetString(codeEntry.scriptName.c_str());
826
827 // line number
828 codeEntry.lineNumber = debugExtractor->GetFristLine(methodId);
829 // lineNumber is 0 means that lineTable error or empty function body, so jump this frame.
830 if (UNLIKELY(codeEntry.lineNumber == 0)) {
831 return false;
832 }
833 codeEntry.columnNumber = debugExtractor->GetFristColumn(methodId);
834
835 traceInfoStack_.emplace_back(codeEntry);
836 stackInfo_.emplace(methodLiteral, codeEntry);
837 return true;
838 }
839
GenerateStringNode(JSTaggedValue entry,size_t size,bool isInFinish)840 Node *HeapSnapshot::GenerateStringNode(JSTaggedValue entry, size_t size, bool isInFinish)
841 {
842 static const CString EMPTY_STRING;
843
844 Node *existNode = entryMap_.FindEntry(reinterpret_cast<Address>(entry.GetTaggedObject())); // Fast Index
845 if (existNode != nullptr) {
846 if (isInFinish) {
847 existNode->SetName(GetString(EntryVisitor::ConvertKey(entry)));
848 }
849 existNode->SetLive(true);
850 return existNode;
851 }
852 // Allocation Event will generate string node for "".
853 // When we need to serialize and isFinish is true, the nodeName will be given the actual string content.
854 auto originStr = static_cast<EcmaString *>(entry.GetTaggedObject());
855 size_t selfsize = (size != 0) ? size : EcmaStringAccessor(originStr).GetFlatStringSize();
856 const CString *nodeName = &EMPTY_STRING;
857 if (isInFinish) {
858 nodeName = GetString(EntryVisitor::ConvertKey(entry));
859 }
860 Address addr = reinterpret_cast<Address>(entry.GetTaggedObject());
861 auto [idExist, sequenceId] = entryIdMap_->FindId(addr);
862 Node *node = Node::NewNode(chunk_, sequenceId, nodeCount_, nodeName, NodeType::PRIM_STRING, selfsize,
863 entry.GetTaggedObject());
864 if (!idExist) {
865 entryIdMap_->InsertId(addr, sequenceId);
866 }
867 entryMap_.InsertEntry(node);
868 InsertNodeUnique(node);
869 return node;
870 }
871
GeneratePrivateStringNode(size_t size)872 Node *HeapSnapshot::GeneratePrivateStringNode(size_t size)
873 {
874 if (privateStringNode_ != nullptr) {
875 return privateStringNode_;
876 }
877 Node *node = nullptr;
878 JSTaggedValue stringValue = vm_->GetJSThread()->GlobalConstants()->GetStringString();
879 auto originStr = static_cast<EcmaString *>(stringValue.GetTaggedObject());
880 size_t selfsize = (size != 0) ? size : EcmaStringAccessor(originStr).GetFlatStringSize();
881 CString strContent;
882 strContent.append(EntryVisitor::ConvertKey(stringValue));
883 Address addr = reinterpret_cast<Address>(stringValue.GetTaggedObject());
884 auto [idExist, sequenceId] = entryIdMap_->FindId(addr);
885 node = Node::NewNode(chunk_, sequenceId, nodeCount_, GetString(strContent), NodeType::PRIM_STRING, selfsize,
886 stringValue.GetTaggedObject());
887 Node *existNode = entryMap_.FindOrInsertNode(node); // Fast Index
888 if (existNode == node) {
889 if (!idExist) {
890 entryIdMap_->InsertId(addr, sequenceId);
891 }
892 entryMap_.InsertEntry(node);
893 InsertNodeUnique(node);
894 } else {
895 existNode->SetLive(true);
896 }
897 ASSERT(entryMap_.FindEntry(node->GetAddress())->GetAddress() == node->GetAddress());
898 if (existNode != node) {
899 if ((node->GetAddress() == existNode->GetAddress()) && (existNode->GetName()->empty())) {
900 existNode->SetName(GetString(strContent));
901 }
902 const_cast<NativeAreaAllocator *>(vm_->GetNativeAreaAllocator())->Delete(node);
903 return nullptr;
904 }
905 privateStringNode_ = node;
906 return node;
907 }
908
FillEdges()909 void HeapSnapshot::FillEdges()
910 {
911 size_t length = nodes_.size();
912 auto iter = nodes_.begin();
913 size_t count = 0;
914 while (++count < length) {
915 ASSERT(*iter != nullptr);
916 auto entryFrom = *iter;
917 auto *objFrom = reinterpret_cast<TaggedObject *>(entryFrom->GetAddress());
918 std::vector<std::pair<CString, JSTaggedValue>> nameResources;
919 JSTaggedValue objValue(objFrom);
920 objValue.DumpForSnapshot(nameResources, isVmMode_);
921 for (auto const &it : nameResources) {
922 JSTaggedValue toValue = it.second;
923 if (toValue.IsNumber() && !captureNumericValue_) {
924 continue;
925 }
926 Node *entryTo = nullptr;
927 if (toValue.IsWeak()) {
928 toValue.RemoveWeakTag();
929 }
930 if (toValue.IsHeapObject()) {
931 auto *to = reinterpret_cast<TaggedObject *>(toValue.GetTaggedObject());
932 entryTo = entryMap_.FindEntry(Node::NewAddress(to));
933 }
934 if (entryTo == nullptr) {
935 entryTo = GenerateNode(toValue, 0, true);
936 }
937 if (entryTo != nullptr) {
938 Edge *edge = Edge::NewEdge(chunk_, edgeCount_, EdgeType::DEFAULT,
939 entryFrom, entryTo, GetString(it.first));
940 RenameFunction(it.first, entryFrom, entryTo);
941 InsertEdgeUnique(edge);
942 (*iter)->IncEdgeCount(); // Update Node's edgeCount_ here
943 }
944 }
945 iter++;
946 }
947 FillPrimitiveEdge(count, iter);
948 }
949
FillPrimitiveEdge(size_t count,CList<Node * >::iterator iter)950 void HeapSnapshot::FillPrimitiveEdge(size_t count, CList<Node *>::iterator iter)
951 {
952 size_t lengthExtend = nodes_.size();
953 while (++count < lengthExtend) {
954 ASSERT(*iter != nullptr);
955 if ((*iter)->GetType() == NodeType::JS_PRIMITIVE_REF) {
956 JSTaggedValue jsFrom(reinterpret_cast<TaggedObject *>((*iter)->GetAddress()));
957 CString valueName;
958 if (jsFrom.IsInt()) {
959 valueName.append(ToCString(jsFrom.GetInt()));
960 } else if (jsFrom.IsDouble()) {
961 valueName.append(FloatToCString(jsFrom.GetDouble()));
962 } else {
963 valueName.append("NaN");
964 }
965 Edge *edge = Edge::NewEdge(chunk_, edgeCount_, EdgeType::DEFAULT, (*iter), (*iter), GetString(valueName));
966 InsertEdgeUnique(edge);
967 (*iter)->IncEdgeCount(); // Update Node's edgeCount_ here
968 }
969 iter++;
970 }
971 }
972
RenameFunction(const CString & edgeName,Node * entryFrom,Node * entryTo)973 void HeapSnapshot::RenameFunction(const CString &edgeName, Node *entryFrom, Node *entryTo)
974 {
975 if (edgeName != "name") {
976 return;
977 }
978 auto fromName = *entryFrom->GetName();
979 if (*entryTo->GetName() != "" && (fromName == "JSFunctionBase" ||
980 fromName == "JSFunction" || fromName == "PromiseValueThunkOrThrowerFunction" ||
981 fromName == "JSGeneratorFunction" || fromName == "PromiseAllSettledElementFunction" ||
982 fromName == "Bound Function" || fromName == "PromiseAnyRejectElementFunction" ||
983 fromName == "PromiseReactionsFunction" || fromName == "PromiseExecutorFunction" ||
984 fromName == "PromiseAllResolveElementFunction" || fromName == "AsyncFunction" ||
985 fromName == "ProxyRevocFunction" || fromName == "AsyncFromSyncIterUnwarpFunction" ||
986 fromName == "PromiseFinallyFunction" || fromName == "JSIntlBoundFunction" ||
987 fromName == "JSAsyncGeneratorFunction" || fromName == "AsyncAwaitStatusFunction")) {
988 entryFrom->SetName(GetString(*entryTo->GetName()));
989 }
990 }
991
BridgeAllReferences()992 void HeapSnapshot::BridgeAllReferences()
993 {
994 // This Function is Unused
995 for (Edge *edge : edges_) {
996 auto *from = reinterpret_cast<TaggedObject *>(edge->GetFrom()->GetAddress());
997 auto *to = reinterpret_cast<TaggedObject *>(edge->GetTo()->GetAddress());
998 if (!JSTaggedValue(from).IsECMAObject()) {
999 continue; // named it by other way
1000 }
1001 edge->SetName(GenerateEdgeName(from, to));
1002 }
1003 }
1004
GenerateEdgeName(TaggedObject * from,TaggedObject * to)1005 CString *HeapSnapshot::GenerateEdgeName([[maybe_unused]] TaggedObject *from, [[maybe_unused]] TaggedObject *to)
1006 {
1007 // This Function is Unused
1008 ASSERT(from != nullptr && from != to);
1009 return GetString("[]"); // unAnalysed
1010 }
1011
InsertNodeUnique(Node * node)1012 Node *HeapSnapshot::InsertNodeUnique(Node *node)
1013 {
1014 AccumulateNodeSize(node->GetSelfSize());
1015 nodes_.emplace_back(node);
1016 nodeCount_++;
1017 return node;
1018 }
1019
EraseNodeUnique(Node * node)1020 void HeapSnapshot::EraseNodeUnique(Node *node)
1021 {
1022 auto iter = std::find(nodes_.begin(), nodes_.end(), node);
1023 if (iter != nodes_.end()) {
1024 DecreaseNodeSize(node->GetSelfSize());
1025 chunk_->Delete(node);
1026 nodes_.erase(iter);
1027 nodeCount_--;
1028 }
1029 }
1030
InsertEdgeUnique(Edge * edge)1031 Edge *HeapSnapshot::InsertEdgeUnique(Edge *edge)
1032 {
1033 edges_.emplace_back(edge);
1034 edgeCount_++;
1035 return edge;
1036 }
1037
AddSyntheticRoot()1038 void HeapSnapshot::AddSyntheticRoot()
1039 {
1040 Node *syntheticRoot = Node::NewNode(chunk_, 1, nodeCount_, GetString("SyntheticRoot"),
1041 NodeType::SYNTHETIC, 0, nullptr);
1042 InsertNodeAt(0, syntheticRoot);
1043
1044 int edgeOffset = 0;
1045 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
1046 #define ROOT_EDGE_BUILDER_CORE(type, slot) \
1047 do { \
1048 JSTaggedValue value((slot).GetTaggedType()); \
1049 if (value.IsHeapObject()) { \
1050 TaggedObject *root = value.GetTaggedObject(); \
1051 Node *rootNode = entryMap_.FindEntry(Node::NewAddress(root)); \
1052 if (rootNode != nullptr) { \
1053 Edge *edge = Edge::NewEdge(chunk_, \
1054 edgeCount_, EdgeType::SHORTCUT, syntheticRoot, rootNode, GetString("-subroot-")); \
1055 InsertEdgeAt(edgeOffset, edge); \
1056 edgeOffset++; \
1057 syntheticRoot->IncEdgeCount(); \
1058 } \
1059 } \
1060 } while (false)
1061
1062 RootVisitor rootEdgeBuilder = [this, syntheticRoot, &edgeOffset]([[maybe_unused]] Root type, ObjectSlot slot) {
1063 ROOT_EDGE_BUILDER_CORE(type, slot);
1064 };
1065 RootBaseAndDerivedVisitor rootBaseEdgeBuilder = []
1066 ([[maybe_unused]] Root type, [[maybe_unused]] ObjectSlot base, [[maybe_unused]] ObjectSlot derived,
1067 [[maybe_unused]] uintptr_t baseOldObject) {
1068 };
1069
1070 RootRangeVisitor rootRangeEdgeBuilder = [this, syntheticRoot, &edgeOffset]([[maybe_unused]] Root type,
1071 ObjectSlot start, ObjectSlot end) {
1072 for (ObjectSlot slot = start; slot < end; slot++) {
1073 ROOT_EDGE_BUILDER_CORE(type, slot);
1074 }
1075 };
1076 #undef ROOT_EDGE_BUILDER_CORE
1077 rootVisitor_.VisitHeapRoots(vm_->GetJSThread(), rootEdgeBuilder, rootRangeEdgeBuilder, rootBaseEdgeBuilder);
1078
1079 int reindex = 0;
1080 for (Node *node : nodes_) {
1081 node->SetIndex(reindex);
1082 reindex++;
1083 }
1084 }
1085
InsertNodeAt(size_t pos,Node * node)1086 Node *HeapSnapshot::InsertNodeAt(size_t pos, Node *node)
1087 {
1088 ASSERT(node != nullptr);
1089 auto iter = nodes_.begin();
1090 std::advance(iter, static_cast<int>(pos));
1091 nodes_.insert(iter, node);
1092 nodeCount_++;
1093 return node;
1094 }
1095
InsertEdgeAt(size_t pos,Edge * edge)1096 Edge *HeapSnapshot::InsertEdgeAt(size_t pos, Edge *edge)
1097 {
1098 ASSERT(edge != nullptr);
1099 auto iter = edges_.begin();
1100 std::advance(iter, static_cast<int>(pos));
1101 edges_.insert(iter, edge);
1102 edgeCount_++;
1103 return edge;
1104 }
1105
ConvertKey(JSTaggedValue key)1106 CString EntryVisitor::ConvertKey(JSTaggedValue key)
1107 {
1108 ASSERT(key.GetTaggedObject() != nullptr);
1109 EcmaString *keyString = EcmaString::Cast(key.GetTaggedObject());
1110
1111 if (key.IsSymbol()) {
1112 JSSymbol *symbol = JSSymbol::Cast(key.GetTaggedObject());
1113 keyString = EcmaString::Cast(symbol->GetDescription().GetTaggedObject());
1114 }
1115 // convert, expensive but safe
1116 return EcmaStringAccessor(keyString).ToCString(StringConvertedUsage::PRINT);
1117 }
1118
FindOrInsertNode(Node * node)1119 Node *HeapEntryMap::FindOrInsertNode(Node *node)
1120 {
1121 ASSERT(node != nullptr);
1122 auto it = nodesMap_.find(node->GetAddress());
1123 if (it != nodesMap_.end()) {
1124 return it->second;
1125 }
1126 InsertEntry(node);
1127 return node;
1128 }
1129
FindAndEraseNode(Address addr)1130 Node *HeapEntryMap::FindAndEraseNode(Address addr)
1131 {
1132 auto it = nodesMap_.find(addr);
1133 if (it != nodesMap_.end()) {
1134 Node *node = it->second;
1135 nodesMap_.erase(it);
1136 nodeEntryCount_--;
1137 return node;
1138 }
1139 return nullptr;
1140 }
1141
FindEntry(Address addr)1142 Node *HeapEntryMap::FindEntry(Address addr)
1143 {
1144 auto it = nodesMap_.find(addr);
1145 return it != nodesMap_.end() ? it->second : nullptr;
1146 }
1147
InsertEntry(Node * node)1148 void HeapEntryMap::InsertEntry(Node *node)
1149 {
1150 nodeEntryCount_++;
1151 nodesMap_.emplace(node->GetAddress(), node);
1152 }
1153
Convert(NodeType type)1154 FrontType NodeTypeConverter::Convert(NodeType type)
1155 {
1156 FrontType fType = FrontType::DEFAULT;
1157 if (type == NodeType::PROPERTY_BOX) {
1158 fType = FrontType::HIDDEN;
1159 } else if (type == NodeType::JS_ARRAY || type == NodeType::JS_TYPED_ARRAY) {
1160 fType = FrontType::ARRAY;
1161 } else if (type == NodeType::PRIM_STRING) { // STRING
1162 fType = FrontType::STRING;
1163 } else if (type == NodeType::JS_OBJECT) {
1164 fType = FrontType::OBJECT;
1165 } else if (type >= NodeType::JS_FUNCTION_FIRST && type <= NodeType::JS_FUNCTION_LAST) {
1166 fType = FrontType::DEFAULT;
1167 } else if (type == NodeType::JS_BOUND_FUNCTION) {
1168 fType = FrontType::DEFAULT;
1169 } else if (type == NodeType::JS_FUNCTION_BASE) {
1170 fType = FrontType::DEFAULT;
1171 } else if (type == NodeType::JS_REG_EXP) {
1172 fType = FrontType::REGEXP;
1173 } else if (type == NodeType::SYMBOL) {
1174 fType = FrontType::SYMBOL;
1175 } else if (type == NodeType::JS_PRIMITIVE_REF) {
1176 fType = FrontType::HEAPNUMBER;
1177 } else if (type == NodeType::SYNTHETIC) {
1178 fType = FrontType::SYNTHETIC;
1179 } else {
1180 fType = FrontType::DEFAULT;
1181 // NATIVE, /* kNative */
1182 // CONSSTRING, /* kConsString */
1183 // SLICEDSTRING, /* kSlicedString */
1184 // SYMBOL, /* kSymbol */
1185 // BIGINT, /* kBigInt */
1186 }
1187 return fType;
1188 }
1189 } // namespace panda::ecmascript
1190