1 /*
2 * Copyright (c) 2023 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/pgo_profiler/pgo_profiler.h"
17
18 #include <chrono>
19 #include <memory>
20
21 #include "ecmascript/enum_conversion.h"
22 #include "ecmascript/interpreter/interpreter-inl.h"
23 #include "ecmascript/jit/jit_profiler.h"
24 #include "ecmascript/pgo_profiler/pgo_profiler_info.h"
25 #include "ecmascript/pgo_profiler/pgo_trace.h"
26
27 namespace panda::ecmascript::pgo {
RecordProfileType(JSHClass *hclass, JSPandaFile *pandaFile, int32_t traceId)28 void PGOProfiler::RecordProfileType(JSHClass *hclass, JSPandaFile *pandaFile, int32_t traceId)
29 {
30 if (!isEnable_) {
31 return;
32 }
33 ProfileType traceType = GetProfileType(hclass);
34 if (traceType.IsNone()) {
35 pgo::ApEntityId abcId(0);
36 pgo::PGOProfilerManager::GetInstance()->GetPandaFileId(pandaFile->GetJSPandaFileDesc(), abcId);
37 SetRootProfileType(hclass, abcId, traceId, ProfileType::Kind::ObjectLiteralId);
38 }
39 }
40
ProfileDefineClass(JSTaggedType ctor)41 void PGOProfiler::ProfileDefineClass(JSTaggedType ctor)
42 {
43 if (!isEnable_) {
44 return;
45 }
46 auto ctorValue = JSTaggedValue(ctor);
47 if (!ctorValue.IsJSFunction()) {
48 return;
49 }
50 auto ctorFunc = JSFunction::Cast(ctorValue.GetTaggedObject());
51 auto ctorMethodValue = ctorFunc->GetMethod();
52 if (!ctorMethodValue.IsMethod()) {
53 return;
54 }
55 auto ctorMethod = Method::Cast(ctorMethodValue);
56 auto entityId = ctorMethod->GetMethodId().GetOffset();
57 if (!InsertDefinedCtor(entityId)) {
58 InsertSkipCtorMethodIdSafe(ctorMethod->GetMethodId());
59 return;
60 }
61
62 auto abcId = GetMethodAbcId(ctorFunc);
63 auto chc = ctorFunc->GetClass();
64 SetRootProfileType(chc, abcId, entityId, ProfileType::Kind::ConstructorId);
65
66 auto protoOrHClass = ctorFunc->GetProtoOrHClass();
67 if (protoOrHClass.IsJSHClass()) {
68 auto ihc = JSHClass::Cast(protoOrHClass.GetTaggedObject());
69 SetRootProfileType(ihc, abcId, entityId, ProfileType::Kind::ClassId);
70 protoOrHClass = ihc->GetProto();
71 }
72 if (protoOrHClass.IsJSObject()) {
73 auto phc = protoOrHClass.GetTaggedObject()->GetClass();
74 SetRootProfileType(phc, abcId, entityId, ProfileType::Kind::PrototypeId);
75 }
76 }
77
ProfileClassRootHClass(JSTaggedType ctor, JSTaggedType rootHcValue, ProfileType::Kind kind)78 void PGOProfiler::ProfileClassRootHClass(JSTaggedType ctor, JSTaggedType rootHcValue, ProfileType::Kind kind)
79 {
80 if (!isEnable_) {
81 return;
82 }
83
84 auto ctorValue = JSTaggedValue(ctor);
85 if (!ctorValue.IsJSFunction()) {
86 return;
87 }
88 auto ctorFunc = JSFunction::Cast(ctorValue.GetTaggedObject());
89 if (!FunctionKindVerify(ctorFunc)) {
90 return;
91 }
92 auto ctorMethodValue = ctorFunc->GetMethod();
93 if (!ctorMethodValue.IsMethod()) {
94 return;
95 }
96 auto ctorMethod = Method::Cast(ctorMethodValue);
97 auto entityId = ctorMethod->GetMethodId().GetOffset();
98 if (IsSkippableCtor(entityId)) {
99 return;
100 }
101
102 auto rootHc = JSHClass::Cast(JSTaggedValue(rootHcValue).GetTaggedObject());
103 auto abcId = GetMethodAbcId(ctorFunc);
104 SetRootProfileType(rootHc, abcId, entityId, kind);
105 }
106
ProfileProtoTransitionClass(JSHandle<JSFunction> func, JSHandle<JSHClass> hclass, JSHandle<JSTaggedValue> proto)107 void PGOProfiler::ProfileProtoTransitionClass(JSHandle<JSFunction> func,
108 JSHandle<JSHClass> hclass,
109 JSHandle<JSTaggedValue> proto)
110 {
111 if (!isEnable_) {
112 return;
113 }
114 auto thread = vm_->GetJSThread();
115 JSHClass *phc = proto->GetTaggedObject()->GetClass();
116 JSHClass *phcRoot = JSHClass::FindRootHClass(phc);
117 auto *transitionTable = thread->GetCurrentEcmaContext()->GetFunctionProtoTransitionTable();
118 JSTaggedType baseIhc = transitionTable->GetFakeParent(JSTaggedType(phcRoot));
119 if (baseIhc == 0) {
120 LOG_ECMA(DEBUG) << "fake parent not found!";
121 ProfileClassRootHClass(func.GetTaggedType(), hclass.GetTaggedType());
122 return;
123 }
124 JSTaggedType ihc = func->GetProtoTransRootHClass().GetRawData();
125 if (JSTaggedValue(ihc).IsUndefined()) {
126 LOG_ECMA(DEBUG) << "maybe the prototype of the current function is just the initial prototype!";
127 ProfileClassRootHClass(func.GetTaggedType(), hclass.GetTaggedType());
128 return;
129 }
130 [[maybe_unused]] bool success = transitionTable->TryInsertFakeParentItem(hclass.GetTaggedType(), ihc);
131 ASSERT(success == true); // ihc wont conflict
132 // record original ihc type
133 ProfileClassRootHClass(func.GetTaggedType(), ihc, ProfileType::Kind::ClassId);
134 // record transition ihc type
135 ProfileClassRootHClass(func.GetTaggedType(), hclass.GetTaggedType(), ProfileType::Kind::TransitionClassId);
136 }
137
ProfileProtoTransitionPrototype(JSHandle<JSFunction> func, JSHandle<JSTaggedValue> prototype, JSHandle<JSTaggedValue> oldPrototype, JSHandle<JSTaggedValue> baseIhc)138 void PGOProfiler::ProfileProtoTransitionPrototype(JSHandle<JSFunction> func,
139 JSHandle<JSTaggedValue> prototype,
140 JSHandle<JSTaggedValue> oldPrototype,
141 JSHandle<JSTaggedValue> baseIhc)
142 {
143 if (!isEnable_) {
144 return;
145 }
146
147 // fuzz test modifies prototype explicitly, add check protection
148 if (!oldPrototype->IsECMAObject()) {
149 return;
150 }
151
152 auto method = func->GetMethod();
153 if (Method::Cast(method)->IsNativeWithCallField()) {
154 return;
155 }
156 // set prototype once, and just skip this time
157 if (!func->GetProtoTransRootHClass().IsUndefined()) {
158 return;
159 }
160 auto thread = vm_->GetJSThread();
161 // insert transition item
162 JSHandle<JSTaggedValue> transIhc(thread, JSTaggedValue::Undefined());
163 JSHandle<JSTaggedValue> transPhc(thread, prototype->GetTaggedObject()->GetClass());
164 if (JSHandle<JSHClass>(baseIhc)->IsDictionaryMode() || JSHandle<JSHClass>(transPhc)->IsDictionaryMode()) {
165 return;
166 }
167 auto *transitionTable = thread->GetCurrentEcmaContext()->GetFunctionProtoTransitionTable();
168 bool success = transitionTable->TryInsertFakeParentItem(transPhc.GetTaggedType(), baseIhc.GetTaggedType());
169 if (!success) {
170 return;
171 }
172 // Do not generate ihc lazily, beacause it's used for the key of hash table
173 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
174 JSHandle<JSHClass> ihc = factory->NewEcmaHClass(JSObject::SIZE, JSType::JS_OBJECT, oldPrototype);
175 func->SetProtoTransRootHClass(thread, JSHandle<JSTaggedValue>(ihc));
176
177 // record phc type
178 JSHClass *phc0Root = JSHClass::FindRootHClass(oldPrototype->GetTaggedObject()->GetClass());
179 ProfileClassRootHClass(func.GetTaggedType(), JSTaggedType(phc0Root), ProfileType::Kind::PrototypeId);
180 ProfileClassRootHClass(func.GetTaggedType(), transPhc.GetTaggedType(), ProfileType::Kind::TransitionPrototypeId);
181 }
182
ProfileDefineGetterSetter(JSHClass* receiverHClass, JSHClass* holderHClass, const JSHandle<JSTaggedValue>& func, int32_t pcOffset)183 void PGOProfiler::ProfileDefineGetterSetter(JSHClass* receiverHClass,
184 JSHClass* holderHClass,
185 const JSHandle<JSTaggedValue>& func,
186 int32_t pcOffset)
187 {
188 if (!isEnable_) {
189 return;
190 }
191 JSTaggedValue funcValue = JSTaggedValue(func.GetTaggedValue());
192 if (!funcValue.IsJSFunction()) {
193 return;
194 }
195 auto methodValue = JSFunction::Cast(funcValue)->GetMethod();
196 if (!methodValue.IsMethod()) {
197 return;
198 }
199
200 JSHandle<JSFunction> function(func);
201
202 WorkNode* workNode = reinterpret_cast<WorkNode*>(function->GetWorkNodePointer());
203 if (workNode != nullptr) {
204 workNode->SetValue(JSTaggedType(JSFunction::Cast(funcValue)));
205 }
206
207 JSHandle<JSTaggedValue> key(vm_->GetJSThread(), JSTaggedValue(pcOffset));
208 JSHandle<JSTaggedValue> receiverHClassHandle(vm_->GetJSThread(), receiverHClass);
209 JSHandle<JSTaggedValue> holderHClassHandle(vm_->GetJSThread(), holderHClass);
210 JSHandle<JSTaggedValue> profileTypeInfoValue(vm_->GetJSThread(), function->GetRawProfileTypeInfo());
211 JSHandle<ProfileTypeInfoCell> profileTypeInfoCell(profileTypeInfoValue);
212
213 if (function->HasProfileTypeInfo(vm_->GetJSThread())) {
214 JSHandle<ProfileTypeInfo> profileTypeInfo(vm_->GetJSThread(), profileTypeInfoCell->GetValue());
215 JSHandle<NumberDictionary> dictJShandle = ProfileTypeInfo::CreateOrGetExtraInfoMap(vm_->GetJSThread(),
216 profileTypeInfo);
217 int entry = dictJShandle->FindEntry(key.GetTaggedValue());
218 if (entry == -1) {
219 ProfileTypeInfo::UpdateExtraInfoMap(vm_->GetJSThread(), dictJShandle, key, receiverHClassHandle,
220 holderHClassHandle, profileTypeInfo);
221 return;
222 }
223 ExtraProfileTypeInfo *mapInfoObj = ExtraProfileTypeInfo::Cast(dictJShandle->GetValue(entry).GetTaggedObject());
224 if (mapInfoObj->GetReceiver() == receiverHClassHandle.GetTaggedValue().CreateAndGetWeakRef() &&
225 mapInfoObj->GetHolder() == holderHClassHandle.GetTaggedValue()) {
226 return;
227 }
228
229 ExtraProfileTypeInfo::Cast(dictJShandle->GetValue(entry).GetTaggedObject())->Clear(vm_->GetJSThread());
230 }
231 }
232
UpdateRootProfileTypeSafe(JSHClass* oldHClass, JSHClass* newHClass)233 void PGOProfiler::UpdateRootProfileTypeSafe(JSHClass* oldHClass, JSHClass* newHClass)
234 {
235 if (!isEnable_) {
236 return;
237 }
238 ProfileType oldPt = GetProfileType(oldHClass);
239 if (oldPt.IsRootType()) {
240 newHClass->SetProfileType(oldPt.GetRaw());
241 oldHClass->SetProfileType(0);
242 }
243 }
244
UpdateTrackElementsKind(JSTaggedValue trackInfoVal, ElementsKind newKind)245 void PGOProfiler::UpdateTrackElementsKind(JSTaggedValue trackInfoVal, ElementsKind newKind)
246 {
247 if (trackInfoVal.IsHeapObject() && trackInfoVal.IsWeak()) {
248 auto trackInfo = TrackInfo::Cast(trackInfoVal.GetWeakReferentUnChecked());
249 auto oldKind = trackInfo->GetElementsKind();
250 if (Elements::IsGeneric(oldKind) || oldKind == newKind) {
251 return;
252 }
253 auto mixKind = Elements::MergeElementsKind(oldKind, newKind);
254 if (mixKind == oldKind) {
255 return;
256 }
257 trackInfo->SetElementsKind(mixKind);
258 auto thread = vm_->GetJSThread();
259 auto globalConst = thread->GlobalConstants();
260 // Since trackinfo is only used at define point,
261 // we update cachedHClass with initial array hclass which does not have IsPrototype set.
262 auto constantId = thread->GetArrayHClassIndexMap().at(mixKind).first;
263 auto hclass = globalConst->GetGlobalConstantObject(static_cast<size_t>(constantId));
264 trackInfo->SetCachedHClass(vm_->GetJSThread(), hclass);
265 UpdateTrackInfo(JSTaggedValue(trackInfo));
266 }
267 }
268
UpdateTrackArrayLength(JSTaggedValue trackInfoVal, uint32_t newSize)269 void PGOProfiler::UpdateTrackArrayLength(JSTaggedValue trackInfoVal, uint32_t newSize)
270 {
271 if (trackInfoVal.IsHeapObject() && trackInfoVal.IsWeak()) {
272 auto trackInfo = TrackInfo::Cast(trackInfoVal.GetWeakReferentUnChecked());
273 uint32_t oldSize = trackInfo->GetArrayLength();
274 if (oldSize >= newSize) {
275 return;
276 }
277 trackInfo->SetArrayLength(newSize);
278 UpdateTrackInfo(JSTaggedValue(trackInfo));
279 }
280 }
281
UpdateTrackSpaceFlag(TaggedObject *object, RegionSpaceFlag spaceFlag)282 void PGOProfiler::UpdateTrackSpaceFlag(TaggedObject *object, RegionSpaceFlag spaceFlag)
283 {
284 if (!object->GetClass()->IsTrackInfoObject()) {
285 return;
286 }
287 auto trackInfo = TrackInfo::Cast(object);
288 RegionSpaceFlag oldFlag = trackInfo->GetSpaceFlag();
289 if (oldFlag == RegionSpaceFlag::IN_YOUNG_SPACE) {
290 trackInfo->SetSpaceFlag(spaceFlag);
291 UpdateTrackInfo(JSTaggedValue(trackInfo));
292 }
293 }
294
UpdateTrackInfo(JSTaggedValue trackInfoVal)295 void PGOProfiler::UpdateTrackInfo(JSTaggedValue trackInfoVal)
296 {
297 if (trackInfoVal.IsHeapObject()) {
298 auto trackInfo = TrackInfo::Cast(trackInfoVal.GetTaggedObject());
299 auto func = trackInfo->GetCachedFunc();
300 auto thread = vm_->GetJSThread();
301 if (!func.IsWeak()) {
302 return;
303 }
304 TaggedObject *object = func.GetWeakReferentUnChecked();
305 if (!object->GetClass()->IsJSFunction()) {
306 return;
307 }
308 JSFunction* function = JSFunction::Cast(object);
309 if (!function->HasProfileTypeInfo(thread)) {
310 return;
311 }
312 auto profileTypeInfoVal = function->GetProfileTypeInfo();
313 if (profileTypeInfoVal.IsUndefined() || !profileTypeInfoVal.IsTaggedArray()) {
314 return;
315 }
316 auto profileTypeInfo = ProfileTypeInfo::Cast(profileTypeInfoVal.GetTaggedObject());
317 if (profileTypeInfo->IsProfileTypeInfoWithBigMethod()) {
318 return;
319 }
320 if (!profileTypeInfo->IsProfileTypeInfoPreDumped()) {
321 profileTypeInfo->SetPreDumpPeriodIndex();
322 PGOPreDump(JSTaggedType(object));
323 }
324 }
325 }
326
PGODump(JSTaggedType func)327 void PGOProfiler::PGODump(JSTaggedType func)
328 {
329 if (!isEnable_ || !vm_->GetJSOptions().IsEnableProfileDump()) {
330 return;
331 }
332
333 auto funcValue = JSTaggedValue(func);
334 if (!funcValue.IsJSFunction()) {
335 return;
336 }
337 auto methodValue = JSFunction::Cast(funcValue)->GetMethod();
338 if (!methodValue.IsMethod()) {
339 return;
340 }
341 auto function = JSFunction::Cast(funcValue);
342 auto workNode = reinterpret_cast<WorkNode *>(function->GetWorkNodePointer());
343 if (workNode == nullptr) {
344 workNode = nativeAreaAllocator_->New<WorkNode>(JSTaggedType(function));
345 function->SetWorkNodePointer(reinterpret_cast<uintptr_t>(workNode));
346 LockHolder lock(mutex_);
347 dumpWorkList_.PushBack(workNode);
348 } else {
349 workNode->SetValue(JSTaggedType(function));
350 auto workList = workNode->GetWorkList();
351 LockHolder lock(mutex_);
352 if (workList == &preDumpWorkList_) {
353 preDumpWorkList_.Remove(workNode);
354 }
355 if (workList != &dumpWorkList_) {
356 dumpWorkList_.PushBack(workNode);
357 }
358 }
359 StartPGODump();
360 }
361
SuspendByGC()362 void PGOProfiler::SuspendByGC()
363 {
364 if (!isEnable_) {
365 return;
366 }
367 LockHolder lock(mutex_);
368 if (GetState() == State::START) {
369 SetState(State::PAUSE);
370 WaitingPGODump();
371 } else if (GetState() == State::FORCE_SAVE) {
372 SetState(State::FORCE_SAVE_PAUSE);
373 WaitingPGODump();
374 }
375 }
376
ResumeByGC()377 void PGOProfiler::ResumeByGC()
378 {
379 if (!isEnable_) {
380 return;
381 }
382 LockHolder lock(mutex_);
383 if (GetState() == State::PAUSE) {
384 SetState(State::START);
385 DispatchPGODumpTask();
386 } else if (GetState() == State::FORCE_SAVE_PAUSE) {
387 SetState(State::FORCE_SAVE);
388 DispatchPGODumpTask();
389 }
390 }
391
StopPGODump()392 void PGOProfiler::StopPGODump()
393 {
394 LockHolder lock(mutex_);
395 if (IsGCWaiting()) {
396 NotifyGC("[StopPGODump::PAUSE]");
397 return;
398 }
399 SetState(State::STOP);
400 NotifyAll("[StopPGODump::STOP]");
401 }
402
StartPGODump()403 void PGOProfiler::StartPGODump()
404 {
405 if (GetState() == State::STOP) {
406 SetState(State::START);
407 DispatchPGODumpTask();
408 }
409 }
410
DispatchPGODumpTask()411 void PGOProfiler::DispatchPGODumpTask()
412 {
413 Taskpool::GetCurrentTaskpool()->PostTask(
414 std::make_unique<PGOProfilerTask>(this, vm_->GetJSThread()->GetThreadId()));
415 }
416
GetState()417 PGOProfiler::State PGOProfiler::GetState()
418 {
419 return state_.load(std::memory_order_acquire);
420 }
421
SetState(State state)422 void PGOProfiler::SetState(State state)
423 {
424 v_.AddLogWithDebugLog("[PGODumpStateChange] " + StateToString(GetState()) + " -> " + StateToString(state));
425 state_.store(state, std::memory_order_release);
426 }
427
NotifyGC(std::string tag)428 void PGOProfiler::NotifyGC(std::string tag)
429 {
430 v_.AddLogWithDebugLog(tag + " notify GC");
431 condition_.SignalAll();
432 }
433
NotifyAll(std::string tag)434 void PGOProfiler::NotifyAll(std::string tag)
435 {
436 v_.AddLogWithDebugLog(tag + " notify all");
437 condition_.SignalAll();
438 }
439
WaitingPGODump()440 void PGOProfiler::WaitingPGODump()
441 {
442 condition_.Wait(&mutex_);
443 }
444
WaitPGODumpFinish()445 void PGOProfiler::WaitPGODumpFinish()
446 {
447 if (!isEnable_) {
448 return;
449 }
450 LockHolder lock(mutex_);
451 while (GetState() == State::START) {
452 WaitingPGODump();
453 }
454 }
455
DumpByForce()456 void PGOProfiler::DumpByForce()
457 {
458 isForce_ = true;
459 LockHolder lock(mutex_);
460 if (GetState() == State::START) {
461 SetState(State::FORCE_SAVE);
462 WaitingPGODump();
463 } else if (GetState() == State::STOP && !dumpWorkList_.IsEmpty()) {
464 SetState(State::FORCE_SAVE);
465 WaitingPGODump();
466 DispatchPGODumpTask();
467 } else if (GetState() == State::PAUSE) {
468 SetState(State::FORCE_SAVE_PAUSE);
469 WaitingPGODump();
470 }
471 }
472
IsGCWaitingWithLock()473 bool PGOProfiler::IsGCWaitingWithLock()
474 {
475 if (GetState() == State::PAUSE) {
476 LockHolder lock(mutex_);
477 if (GetState() == State::PAUSE) {
478 return true;
479 }
480 }
481 return false;
482 }
483
IsGCWaiting()484 bool PGOProfiler::IsGCWaiting()
485 {
486 if (GetState() == State::PAUSE) {
487 return true;
488 }
489 return false;
490 }
491
PGOPreDump(JSTaggedType func)492 void PGOProfiler::PGOPreDump(JSTaggedType func)
493 {
494 if (!isEnable_ || !vm_->GetJSOptions().IsEnableProfileDump()) {
495 return;
496 }
497
498 auto funcValue = JSTaggedValue(func);
499 if (!funcValue.IsJSFunction()) {
500 return;
501 }
502 auto methodValue = JSFunction::Cast(funcValue)->GetMethod();
503 if (!methodValue.IsMethod()) {
504 return;
505 }
506 auto function = JSFunction::Cast(funcValue);
507 auto workNode = reinterpret_cast<WorkNode *>(function->GetWorkNodePointer());
508 if (workNode == nullptr) {
509 workNode = nativeAreaAllocator_->New<WorkNode>(JSTaggedType(function));
510 function->SetWorkNodePointer(reinterpret_cast<uintptr_t>(workNode));
511 LockHolder lock(mutex_);
512 preDumpWorkList_.PushBack(workNode);
513 } else {
514 workNode->SetValue(JSTaggedType(function));
515 auto workList = workNode->GetWorkList();
516 LockHolder lock(mutex_);
517 if (workList == &dumpWorkList_) {
518 workList->Remove(workNode);
519 }
520 if (workList != &preDumpWorkList_) {
521 preDumpWorkList_.PushBack(workNode);
522 }
523 }
524 }
525
UpdateExtraProfileTypeInfo(ApEntityId abcId, const CString& recordName, EntityId methodId, WorkNode* current)526 void PGOProfiler::UpdateExtraProfileTypeInfo(ApEntityId abcId,
527 const CString& recordName,
528 EntityId methodId,
529 WorkNode* current)
530 {
531 JSTaggedValue funcValue = JSTaggedValue(current->GetValue());
532 if (!funcValue.IsJSFunction()) {
533 return;
534 }
535 auto func = JSFunction::Cast(funcValue);
536 if (!func->HasProfileTypeInfo(vm_->GetJSThread())) {
537 return;
538 }
539 ProfileTypeInfoCell *cell = ProfileTypeInfoCell::Cast(func->GetRawProfileTypeInfo());
540 ProfileTypeInfo *info = ProfileTypeInfo::Cast((cell->GetValue()).GetTaggedObject());
541 if ((info->GetExtraInfoMap()).IsHole() || (info->GetExtraInfoMap()).IsUndefined()) {
542 return;
543 }
544 NumberDictionary *dict = NumberDictionary::Cast(info->GetExtraInfoMap().GetTaggedObject());
545 int size = dict->Size();
546 for (int hashIndex = 0; hashIndex < size; hashIndex++) {
547 JSTaggedValue key(dict->GetKey(hashIndex));
548 if (!key.IsUndefined() && !key.IsHole()) {
549 JSTaggedValue val(dict->GetValue(hashIndex));
550 ExtraProfileTypeInfo *extraInfo = ExtraProfileTypeInfo::Cast(val.GetTaggedObject());
551 if (!extraInfo->IsValid()) {
552 continue;
553 }
554 AddObjectInfo(abcId,
555 recordName,
556 methodId,
557 key.GetInt(),
558 extraInfo->GetReceiverHClass(),
559 extraInfo->GetReceiverHClass(),
560 extraInfo->GetHolderHClass());
561 }
562 }
563 }
564
HasValidExtraProfileTypeInfo(JSFunction *func)565 bool PGOProfiler::HasValidExtraProfileTypeInfo(JSFunction *func)
566 {
567 if (!func->HasProfileTypeInfo(vm_->GetJSThread())) {
568 return false;
569 }
570 ProfileTypeInfoCell *profileCell = ProfileTypeInfoCell::Cast(func->GetRawProfileTypeInfo());
571 ProfileTypeInfo *profileInfo = ProfileTypeInfo::Cast((profileCell->GetValue()).GetTaggedObject());
572 JSTaggedValue map = profileInfo->GetExtraInfoMap();
573 if (map.IsHole() || map.IsUndefined()) {
574 return false;
575 }
576 NumberDictionary *numberDict = NumberDictionary::Cast(map.GetTaggedObject());
577 return numberDict->GetEntrySize() > 0;
578 }
579
ProcessExtraProfileTypeInfo(JSFunction *func, ApEntityId abcId, const CString &recordName, JSTaggedValue methodValue, WorkNode *current)580 void PGOProfiler::ProcessExtraProfileTypeInfo(JSFunction *func, ApEntityId abcId, const CString &recordName,
581 JSTaggedValue methodValue, WorkNode *current)
582 {
583 if (!HasValidExtraProfileTypeInfo(func)) {
584 return;
585 }
586 Method* method = Method::Cast(methodValue.GetTaggedObject());
587 EntityId methodId = method->GetMethodId();
588 UpdateExtraProfileTypeInfo(abcId, recordName, methodId, current);
589 }
590
HandlePGOPreDump()591 void PGOProfiler::HandlePGOPreDump()
592 {
593 LockHolder lock(recordInfoMutex_);
594 if (!isEnable_ || !vm_->GetJSOptions().IsEnableProfileDump()) {
595 return;
596 }
597 DISALLOW_GARBAGE_COLLECTION;
598 preDumpWorkList_.Iterate([this](WorkNode* current) {
599 JSTaggedValue funcValue = JSTaggedValue(current->GetValue());
600 if (!funcValue.IsJSFunction()) {
601 return;
602 }
603 auto func = JSFunction::Cast(funcValue);
604 if (func->IsSendableOrConcurrentFunction()) {
605 return;
606 }
607 JSTaggedValue methodValue = func->GetMethod();
608 if (!methodValue.IsMethod()) {
609 return;
610 }
611 CString recordName = func->GetRecordName();
612 if (recordName.empty()) {
613 return;
614 }
615 auto abcId = GetMethodAbcId(func);
616
617 ProcessExtraProfileTypeInfo(func, abcId, recordName, methodValue, current);
618 ProfileType recordType = GetRecordProfileType(abcId, recordName);
619 recordInfos_->AddMethod(recordType, Method::Cast(methodValue), SampleMode::HOTNESS_MODE);
620 ProfileBytecode(abcId, recordName, funcValue);
621 if (PGOTrace::GetInstance()->IsEnable()) {
622 PGOTrace::GetInstance()->TryGetMethodData(methodValue, false);
623 }
624 });
625 }
626
HandlePGODumpByDumpThread(bool force)627 void PGOProfiler::HandlePGODumpByDumpThread(bool force)
628 {
629 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PGOProfiler::HandlePGODumpByDumpThread");
630 LockHolder lock(recordInfoMutex_);
631 if (!isEnable_ || !vm_->GetJSOptions().IsEnableProfileDump()) {
632 return;
633 }
634 DISALLOW_GARBAGE_COLLECTION;
635 auto current = PopFromProfileQueue();
636 while (current != nullptr) {
637 JSTaggedValue value = JSTaggedValue(current->GetValue());
638 if (value.IsUndefined()) {
639 current = PopFromProfileQueue();
640 continue;
641 }
642 if (!value.IsJSFunction()) {
643 current = PopFromProfileQueue();
644 continue;
645 }
646 auto func = JSFunction::Cast(value);
647 if (func->IsSendableOrConcurrentFunction()) {
648 current = PopFromProfileQueue();
649 continue;
650 }
651 JSTaggedValue methodValue = func->GetMethod();
652 if (!methodValue.IsMethod()) {
653 current = PopFromProfileQueue();
654 continue;
655 }
656 CString recordName = func->GetRecordName();
657 if (recordName.empty()) {
658 current = PopFromProfileQueue();
659 continue;
660 }
661 auto abcId = GetMethodAbcId(func);
662
663 ProcessExtraProfileTypeInfo(func, abcId, recordName, methodValue, current);
664
665 ProfileType recordType = GetRecordProfileType(abcId, recordName);
666 if (recordInfos_->AddMethod(recordType, Method::Cast(methodValue), SampleMode::HOTNESS_MODE)) {
667 methodCount_++;
668 }
669 ProfileBytecode(abcId, recordName, value);
670 current = PopFromProfileQueue();
671 if (PGOTrace::GetInstance()->IsEnable()) {
672 PGOTrace::GetInstance()->TryGetMethodData(methodValue, true);
673 }
674 }
675 ASSERT(GetState() != State::STOP);
676 if (IsGCWaitingWithLock()) {
677 return;
678 }
679 MergeProfilerAndDispatchAsyncSaveTask(force);
680 }
681
MergeProfilerAndDispatchAsyncSaveTask(bool force)682 void PGOProfiler::MergeProfilerAndDispatchAsyncSaveTask(bool force)
683 {
684 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PGOProfiler::MergeProfilerAndDispatchAsyncSaveTask");
685 // Merged every 50 methods and merge interval greater than minimal interval
686 auto interval = std::chrono::system_clock::now() - saveTimestamp_;
687 auto minIntervalOption = vm_->GetJSOptions().GetPGOSaveMinInterval();
688 auto mergeMinInterval = std::chrono::milliseconds(minIntervalOption * MS_PRE_SECOND);
689 if ((methodCount_ >= MERGED_EVERY_COUNT && interval > mergeMinInterval) || (force && methodCount_ > 0)) {
690 LOG_ECMA(DEBUG) << "Sample: post task to save profiler";
691 {
692 ClockScope start;
693 PGOProfilerManager::GetInstance()->Merge(this);
694 if (PGOTrace::GetInstance()->IsEnable()) {
695 PGOTrace::GetInstance()->SetMergeTime(start.TotalSpentTime());
696 }
697 }
698 if (!force) {
699 PGOProfilerManager::GetInstance()->AsyncSave();
700 }
701 SetSaveTimestamp(std::chrono::system_clock::now());
702 methodCount_ = 0;
703 }
704 }
705
PopFromProfileQueue()706 PGOProfiler::WorkNode* PGOProfiler::PopFromProfileQueue()
707 {
708 LockHolder lock(mutex_);
709 WorkNode* node = nullptr;
710 while (node == nullptr) {
711 if (IsGCWaiting()) {
712 break;
713 }
714 if (dumpWorkList_.IsEmpty()) {
715 break;
716 }
717 node = dumpWorkList_.PopFront();
718 }
719 return node;
720 }
721
ProfileBytecode(ApEntityId abcId, const CString &recordName, JSTaggedValue funcValue)722 void PGOProfiler::ProfileBytecode(ApEntityId abcId, const CString &recordName, JSTaggedValue funcValue)
723 {
724 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PGOProfiler::ProfileBytecode");
725 ClockScope start;
726 JSFunction *function = JSFunction::Cast(funcValue);
727 if (function->IsSendableOrConcurrentFunction()) {
728 return;
729 }
730 Method *method = Method::Cast(function->GetMethod());
731 JSTaggedValue profileTypeInfoVal = function->GetProfileTypeInfo();
732 ASSERT(!profileTypeInfoVal.IsUndefined());
733 auto profileTypeInfo = ProfileTypeInfo::Cast(profileTypeInfoVal.GetTaggedObject());
734 auto methodId = method->GetMethodId();
735 auto pcStart = method->GetBytecodeArray();
736 auto codeSize = method->GetCodeSize();
737 BytecodeInstruction bcIns(pcStart);
738 auto bcInsLast = bcIns.JumpTo(codeSize);
739 bool isForceDump = vm_->GetJSOptions().IsPgoForceDump();
740
741 while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
742 if (!isForceDump) {
743 if (IsGCWaitingWithLock()) {
744 break;
745 }
746 }
747 auto opcode = bcIns.GetOpcode();
748 auto bcOffset = bcIns.GetAddress() - pcStart;
749 auto pc = bcIns.GetAddress();
750 switch (opcode) {
751 case EcmaOpcode::LDTHISBYNAME_IMM8_ID16:
752 case EcmaOpcode::LDOBJBYNAME_IMM8_ID16:
753 case EcmaOpcode::LDPRIVATEPROPERTY_IMM8_IMM16_IMM16: {
754 uint8_t slotId = READ_INST_8_0();
755 CHECK_SLOTID_BREAK(slotId);
756 DumpICByName(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::LOAD);
757 break;
758 }
759 case EcmaOpcode::LDTHISBYNAME_IMM16_ID16:
760 case EcmaOpcode::LDOBJBYNAME_IMM16_ID16: {
761 uint16_t slotId = READ_INST_16_0();
762 DumpICByName(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::LOAD);
763 break;
764 }
765 case EcmaOpcode::LDOBJBYVALUE_IMM8_V8:
766 case EcmaOpcode::LDTHISBYVALUE_IMM8: {
767 uint8_t slotId = READ_INST_8_0();
768 CHECK_SLOTID_BREAK(slotId);
769 DumpICByValue(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::LOAD);
770 break;
771 }
772 case EcmaOpcode::LDOBJBYVALUE_IMM16_V8:
773 case EcmaOpcode::LDTHISBYVALUE_IMM16: {
774 uint16_t slotId = READ_INST_16_0();
775 DumpICByValue(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::LOAD);
776 break;
777 }
778 case EcmaOpcode::STOBJBYNAME_IMM8_ID16_V8:
779 case EcmaOpcode::STTHISBYNAME_IMM8_ID16:
780 case EcmaOpcode::DEFINEPROPERTYBYNAME_IMM8_ID16_V8:
781 case EcmaOpcode::STPRIVATEPROPERTY_IMM8_IMM16_IMM16_V8: {
782 uint8_t slotId = READ_INST_8_0();
783 CHECK_SLOTID_BREAK(slotId);
784 DumpICByName(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::STORE);
785 break;
786 }
787 case EcmaOpcode::STOBJBYNAME_IMM16_ID16_V8:
788 case EcmaOpcode::STTHISBYNAME_IMM16_ID16: {
789 uint16_t slotId = READ_INST_16_0();
790 DumpICByName(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::STORE);
791 break;
792 }
793 case EcmaOpcode::STOBJBYVALUE_IMM8_V8_V8:
794 case EcmaOpcode::STOWNBYINDEX_IMM8_V8_IMM16:
795 case EcmaOpcode::STTHISBYVALUE_IMM8_V8: {
796 uint8_t slotId = READ_INST_8_0();
797 CHECK_SLOTID_BREAK(slotId);
798 DumpICByValue(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::STORE);
799 break;
800 }
801 case EcmaOpcode::STOBJBYVALUE_IMM16_V8_V8:
802 case EcmaOpcode::STOWNBYINDEX_IMM16_V8_IMM16:
803 case EcmaOpcode::STTHISBYVALUE_IMM16_V8: {
804 uint16_t slotId = READ_INST_16_0();
805 DumpICByValue(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, BCType::STORE);
806 break;
807 }
808 // Op
809 case EcmaOpcode::ADD2_IMM8_V8:
810 case EcmaOpcode::SUB2_IMM8_V8:
811 case EcmaOpcode::MUL2_IMM8_V8:
812 case EcmaOpcode::DIV2_IMM8_V8:
813 case EcmaOpcode::MOD2_IMM8_V8:
814 case EcmaOpcode::SHL2_IMM8_V8:
815 case EcmaOpcode::SHR2_IMM8_V8:
816 case EcmaOpcode::AND2_IMM8_V8:
817 case EcmaOpcode::OR2_IMM8_V8:
818 case EcmaOpcode::XOR2_IMM8_V8:
819 case EcmaOpcode::ASHR2_IMM8_V8:
820 case EcmaOpcode::EXP_IMM8_V8:
821 case EcmaOpcode::NEG_IMM8:
822 case EcmaOpcode::NOT_IMM8:
823 case EcmaOpcode::INC_IMM8:
824 case EcmaOpcode::DEC_IMM8:
825 case EcmaOpcode::EQ_IMM8_V8:
826 case EcmaOpcode::NOTEQ_IMM8_V8:
827 case EcmaOpcode::LESS_IMM8_V8:
828 case EcmaOpcode::LESSEQ_IMM8_V8:
829 case EcmaOpcode::GREATER_IMM8_V8:
830 case EcmaOpcode::GREATEREQ_IMM8_V8:
831 case EcmaOpcode::STRICTNOTEQ_IMM8_V8:
832 case EcmaOpcode::STRICTEQ_IMM8_V8:
833 case EcmaOpcode::TONUMERIC_IMM8: {
834 uint8_t slotId = READ_INST_8_0();
835 CHECK_SLOTID_BREAK(slotId);
836 DumpOpType(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo);
837 break;
838 }
839 case EcmaOpcode::CALLRUNTIME_ISTRUE_PREF_IMM8:
840 case EcmaOpcode::CALLRUNTIME_ISFALSE_PREF_IMM8: {
841 uint8_t slotId = READ_INST_8_1();
842 CHECK_SLOTID_BREAK(slotId);
843 DumpOpType(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo);
844 break;
845 }
846 // Call
847 case EcmaOpcode::CALLARG0_IMM8:
848 case EcmaOpcode::CALLARG1_IMM8_V8:
849 case EcmaOpcode::CALLARGS2_IMM8_V8_V8:
850 case EcmaOpcode::CALLARGS3_IMM8_V8_V8_V8:
851 case EcmaOpcode::CALLRANGE_IMM8_IMM8_V8:
852 case EcmaOpcode::CALLTHIS0_IMM8_V8:
853 case EcmaOpcode::CALLTHIS1_IMM8_V8_V8:
854 case EcmaOpcode::CALLTHIS2_IMM8_V8_V8_V8:
855 case EcmaOpcode::CALLTHIS3_IMM8_V8_V8_V8_V8:
856 case EcmaOpcode::CALLTHISRANGE_IMM8_IMM8_V8: {
857 uint8_t slotId = READ_INST_8_0();
858 CHECK_SLOTID_BREAK(slotId);
859 DumpCall(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo);
860 break;
861 }
862 case EcmaOpcode::CALLRUNTIME_CALLINIT_PREF_IMM8_V8: {
863 uint8_t slotId = READ_INST_8_1();
864 CHECK_SLOTID_BREAK(slotId);
865 DumpCall(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo);
866 break;
867 }
868 case EcmaOpcode::WIDE_CALLRANGE_PREF_IMM16_V8:
869 case EcmaOpcode::WIDE_CALLTHISRANGE_PREF_IMM16_V8: {
870 // no ic slot
871 break;
872 }
873 case EcmaOpcode::NEWOBJRANGE_IMM8_IMM8_V8: {
874 uint8_t slotId = READ_INST_8_0();
875 CHECK_SLOTID_BREAK(slotId);
876 DumpNewObjRange(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo);
877 break;
878 }
879 case EcmaOpcode::NEWOBJRANGE_IMM16_IMM8_V8: {
880 uint16_t slotId = READ_INST_16_0();
881 DumpNewObjRange(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo);
882 break;
883 }
884 case EcmaOpcode::WIDE_NEWOBJRANGE_PREF_IMM16_V8: {
885 break;
886 }
887 // Create object
888 case EcmaOpcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8: {
889 uint8_t slotId = READ_INST_8_0();
890 CHECK_SLOTID_BREAK(slotId);
891 DumpDefineClass(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo);
892 break;
893 }
894 case EcmaOpcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8: {
895 uint16_t slotId = READ_INST_16_0();
896 DumpDefineClass(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo);
897 break;
898 }
899 case EcmaOpcode::DEFINEFUNC_IMM8_ID16_IMM8: {
900 uint8_t slotId = READ_INST_8_0();
901 CHECK_SLOTID_BREAK(slotId);
902 DumpDefineClass(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo);
903 break;
904 }
905 case EcmaOpcode::DEFINEFUNC_IMM16_ID16_IMM8: {
906 uint16_t slotId = READ_INST_16_0();
907 DumpDefineClass(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo);
908 break;
909 }
910 case EcmaOpcode::CREATEOBJECTWITHBUFFER_IMM8_ID16:
911 case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_ID16:
912 case EcmaOpcode::CREATEEMPTYARRAY_IMM8: {
913 if (method->GetJSPandaFile() == nullptr) {
914 break;
915 }
916 auto header = method->GetJSPandaFile()->GetPandaFile()->GetHeader();
917 auto traceId =
918 static_cast<int32_t>(reinterpret_cast<uintptr_t>(pc) - reinterpret_cast<uintptr_t>(header));
919 uint8_t slotId = READ_INST_8_0();
920 CHECK_SLOTID_BREAK(slotId);
921 DumpCreateObject(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, traceId);
922 break;
923 }
924 case EcmaOpcode::CREATEOBJECTWITHBUFFER_IMM16_ID16:
925 case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM16_ID16:
926 case EcmaOpcode::CREATEEMPTYARRAY_IMM16: {
927 if (method->GetJSPandaFile() == nullptr) {
928 break;
929 }
930 auto header = method->GetJSPandaFile()->GetPandaFile()->GetHeader();
931 auto traceId =
932 static_cast<int32_t>(reinterpret_cast<uintptr_t>(pc) - reinterpret_cast<uintptr_t>(header));
933 uint16_t slotId = READ_INST_16_0();
934 DumpCreateObject(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo, traceId);
935 break;
936 }
937 case EcmaOpcode::GETITERATOR_IMM8: {
938 uint8_t slotId = READ_INST_8_0();
939 CHECK_SLOTID_BREAK(slotId);
940 DumpGetIterator(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo);
941 break;
942 }
943 case EcmaOpcode::GETITERATOR_IMM16: {
944 uint16_t slotId = READ_INST_16_0();
945 DumpGetIterator(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo);
946 break;
947 }
948 // Others
949 case EcmaOpcode::INSTANCEOF_IMM8_V8: {
950 uint8_t slotId = READ_INST_8_0();
951 CHECK_SLOTID_BREAK(slotId);
952 DumpInstanceof(abcId, recordName, methodId, bcOffset, slotId, profileTypeInfo);
953 break;
954 }
955 case EcmaOpcode::DEFINEGETTERSETTERBYVALUE_V8_V8_V8_V8:
956 default:
957 break;
958 }
959 bcIns = bcIns.GetNext();
960 }
961 if (PGOTrace::GetInstance()->IsEnable()) {
962 auto methodData = PGOTrace::GetInstance()->TryGetMethodData(function->GetMethod());
963 methodData->SetProfileBytecodeTime(start.TotalSpentTime());
964 }
965 }
966
DumpICByName(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, ProfileTypeInfo *profileTypeInfo, BCType type)967 void PGOProfiler::DumpICByName(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset,
968 uint32_t slotId, ProfileTypeInfo *profileTypeInfo, BCType type)
969 {
970 JSTaggedValue firstValue = profileTypeInfo->Get(slotId);
971 if (!firstValue.IsHeapObject()) {
972 if (firstValue.IsHole()) {
973 // Mega state
974 AddObjectInfoWithMega(abcId, recordName, methodId, bcOffset);
975 }
976 return;
977 }
978 if (firstValue.IsWeak()) {
979 TaggedObject *object = firstValue.GetWeakReferentUnChecked();
980 if (object->GetClass()->IsHClass()) {
981 JSTaggedValue secondValue = profileTypeInfo->Get(slotId + 1);
982 JSHClass *hclass = JSHClass::Cast(object);
983 DumpICByNameWithHandler(abcId, recordName, methodId, bcOffset, hclass, secondValue, type);
984 }
985 return;
986 }
987 DumpICByNameWithPoly(abcId, recordName, methodId, bcOffset, firstValue, type);
988 }
989
DumpICByValue(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, ProfileTypeInfo *profileTypeInfo, BCType type)990 void PGOProfiler::DumpICByValue(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset,
991 uint32_t slotId, ProfileTypeInfo *profileTypeInfo, BCType type)
992 {
993 JSTaggedValue firstValue = profileTypeInfo->Get(slotId);
994 if (!firstValue.IsHeapObject()) {
995 if (firstValue.IsHole()) {
996 // Mega state
997 AddObjectInfoWithMega(abcId, recordName, methodId, bcOffset);
998 }
999 return;
1000 }
1001 if (firstValue.IsWeak()) {
1002 TaggedObject *object = firstValue.GetWeakReferentUnChecked();
1003 if (object->GetClass()->IsHClass()) {
1004 JSTaggedValue secondValue = profileTypeInfo->Get(slotId + 1);
1005 JSHClass *hclass = JSHClass::Cast(object);
1006 DumpICByValueWithHandler(abcId, recordName, methodId, bcOffset, hclass, secondValue, type);
1007 }
1008 return;
1009 }
1010 // Check key
1011 if ((firstValue.IsString() || firstValue.IsSymbol())) {
1012 return;
1013 }
1014 // Check without key
1015 DumpICByValueWithPoly(abcId, recordName, methodId, bcOffset, firstValue, type);
1016 }
1017
DumpICByNameWithPoly(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, JSTaggedValue cacheValue, BCType type)1018 void PGOProfiler::DumpICByNameWithPoly(ApEntityId abcId,
1019 const CString &recordName, EntityId methodId, int32_t bcOffset, JSTaggedValue cacheValue, BCType type)
1020 {
1021 if (cacheValue.IsWeak()) {
1022 return;
1023 }
1024 ASSERT(cacheValue.IsTaggedArray());
1025 auto array = TaggedArray::Cast(cacheValue);
1026 uint32_t length = array->GetLength();
1027 for (uint32_t i = 0; i < length; i += 2) { // 2 means one ic, two slot
1028 auto result = array->Get(i);
1029 auto handler = array->Get(i + 1);
1030 if (!result.IsHeapObject() || !result.IsWeak()) {
1031 continue;
1032 }
1033 TaggedObject *object = result.GetWeakReferentUnChecked();
1034 if (!object->GetClass()->IsHClass()) {
1035 continue;
1036 }
1037 JSHClass *hclass = JSHClass::Cast(object);
1038 if (!DumpICByNameWithHandler(abcId, recordName, methodId, bcOffset, hclass, handler, type)) {
1039 AddObjectInfoWithMega(abcId, recordName, methodId, bcOffset);
1040 break;
1041 }
1042 }
1043 }
1044
DumpICByValueWithPoly(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, JSTaggedValue cacheValue, BCType type)1045 void PGOProfiler::DumpICByValueWithPoly(ApEntityId abcId,
1046 const CString &recordName, EntityId methodId, int32_t bcOffset, JSTaggedValue cacheValue, BCType type)
1047 {
1048 if (cacheValue.IsWeak()) {
1049 return;
1050 }
1051 ASSERT(cacheValue.IsTaggedArray());
1052 auto array = TaggedArray::Cast(cacheValue);
1053 uint32_t length = array->GetLength();
1054 for (uint32_t i = 0; i < length; i += 2) { // 2 means one ic, two slot
1055 auto result = array->Get(i);
1056 auto handler = array->Get(i + 1);
1057 if (!result.IsHeapObject() || !result.IsWeak()) {
1058 continue;
1059 }
1060 TaggedObject *object = result.GetWeakReferentUnChecked();
1061 if (!object->GetClass()->IsHClass()) {
1062 continue;
1063 }
1064 JSHClass *hclass = JSHClass::Cast(object);
1065 DumpICByValueWithHandler(abcId, recordName, methodId, bcOffset, hclass, handler, type);
1066 }
1067 }
1068
DumpICByNameWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, JSHClass *hclass, JSTaggedValue secondValue, BCType type)1069 bool PGOProfiler::DumpICByNameWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId,
1070 int32_t bcOffset, JSHClass *hclass, JSTaggedValue secondValue, BCType type)
1071 {
1072 TryDumpProtoTransitionType(hclass);
1073 if (type == BCType::LOAD) {
1074 return DumpICLoadByNameWithHandler(abcId, recordName, methodId, bcOffset, hclass, secondValue);
1075 }
1076
1077 if (secondValue.IsInt()) {
1078 return AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, hclass);
1079 } else if (secondValue.IsTransitionHandler()) {
1080 auto transitionHandler = TransitionHandler::Cast(secondValue.GetTaggedObject());
1081 auto transitionHClassVal = transitionHandler->GetTransitionHClass();
1082 if (transitionHClassVal.IsJSHClass()) {
1083 auto transitionHClass = JSHClass::Cast(transitionHClassVal.GetTaggedObject());
1084 return AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, transitionHClass);
1085 }
1086 } else if (secondValue.IsTransWithProtoHandler()) {
1087 auto transWithProtoHandler = TransWithProtoHandler::Cast(secondValue.GetTaggedObject());
1088 auto cellValue = transWithProtoHandler->GetProtoCell();
1089 if (CheckProtoChangeMarker(cellValue)) {
1090 return false;
1091 }
1092 auto transitionHClassVal = transWithProtoHandler->GetTransitionHClass();
1093 if (transitionHClassVal.IsJSHClass()) {
1094 auto transitionHClass = JSHClass::Cast(transitionHClassVal.GetTaggedObject());
1095 return AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, transitionHClass);
1096 }
1097 } else if (secondValue.IsPrototypeHandler()) {
1098 auto prototypeHandler = PrototypeHandler::Cast(secondValue.GetTaggedObject());
1099 auto cellValue = prototypeHandler->GetProtoCell();
1100 if (CheckProtoChangeMarker(cellValue)) {
1101 return false;
1102 }
1103 auto holder = prototypeHandler->GetHolder();
1104 auto holderHClass = holder.GetTaggedObject()->GetClass();
1105 auto accessorMethodId = prototypeHandler->GetAccessorMethodId();
1106 return AddObjectInfo(
1107 abcId, recordName, methodId, bcOffset, hclass, holderHClass, holderHClass, accessorMethodId);
1108 } else if (secondValue.IsStoreTSHandler()) {
1109 StoreTSHandler *storeTSHandler = StoreTSHandler::Cast(secondValue.GetTaggedObject());
1110 auto cellValue = storeTSHandler->GetProtoCell();
1111 if (CheckProtoChangeMarker(cellValue)) {
1112 return false;
1113 }
1114 auto holder = storeTSHandler->GetHolder();
1115 auto holderHClass = holder.GetTaggedObject()->GetClass();
1116 return AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, holderHClass, holderHClass);
1117 }
1118 // StoreGlobal
1119 return false;
1120 }
1121
DumpICLoadByNameWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, JSHClass *hclass, JSTaggedValue secondValue)1122 bool PGOProfiler::DumpICLoadByNameWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId,
1123 int32_t bcOffset, JSHClass *hclass, JSTaggedValue secondValue)
1124 {
1125 bool ret = false;
1126 if (secondValue.IsInt()) {
1127 auto handlerInfo = static_cast<uint32_t>(secondValue.GetInt());
1128 if (HandlerBase::IsNonExist(handlerInfo)) {
1129 return ret;
1130 }
1131 if (HandlerBase::IsField(handlerInfo) || HandlerBase::IsAccessor(handlerInfo)) {
1132 if (AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, hclass)) {
1133 return true;
1134 }
1135 }
1136 return AddBuiltinsInfoByNameInInstance(abcId, recordName, methodId, bcOffset, hclass);
1137 } else if (secondValue.IsPrototypeHandler()) {
1138 auto prototypeHandler = PrototypeHandler::Cast(secondValue.GetTaggedObject());
1139 auto cellValue = prototypeHandler->GetProtoCell();
1140 if (CheckProtoChangeMarker(cellValue)) {
1141 return ret;
1142 }
1143 auto holder = prototypeHandler->GetHolder();
1144 auto holderHClass = holder.GetTaggedObject()->GetClass();
1145 JSTaggedValue handlerInfoVal = prototypeHandler->GetHandlerInfo();
1146 if (!handlerInfoVal.IsInt()) {
1147 return ret;
1148 }
1149 auto handlerInfo = static_cast<uint32_t>(handlerInfoVal.GetInt());
1150 if (HandlerBase::IsNonExist(handlerInfo)) {
1151 return ret;
1152 }
1153 auto accessorMethodId = prototypeHandler->GetAccessorMethodId();
1154 if (!AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, holderHClass,
1155 holderHClass, accessorMethodId)) {
1156 return AddBuiltinsInfoByNameInProt(abcId, recordName, methodId, bcOffset, hclass, holderHClass);
1157 }
1158 return true;
1159 }
1160 // LoadGlobal
1161 return false;
1162 }
1163
DumpICByValueWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, JSHClass *hclass, JSTaggedValue secondValue, BCType type)1164 void PGOProfiler::DumpICByValueWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId,
1165 int32_t bcOffset, JSHClass *hclass, JSTaggedValue secondValue, BCType type)
1166 {
1167 TryDumpProtoTransitionType(hclass);
1168 if (type == BCType::LOAD) {
1169 if (secondValue.IsInt()) {
1170 auto handlerInfo = static_cast<uint32_t>(secondValue.GetInt());
1171 if (HandlerBase::IsNormalElement(handlerInfo) || HandlerBase::IsStringElement(handlerInfo)) {
1172 if (HandlerBase::NeedSkipInPGODump(handlerInfo)) {
1173 return;
1174 }
1175 AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, hclass, hclass);
1176 return;
1177 }
1178
1179 if (HandlerBase::IsTypedArrayElement(handlerInfo)) {
1180 OnHeapMode onHeap = HandlerBase::IsOnHeap(handlerInfo) ? OnHeapMode::ON_HEAP : OnHeapMode::NOT_ON_HEAP;
1181 AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, onHeap);
1182 return;
1183 }
1184
1185 AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, hclass);
1186 }
1187 return;
1188 }
1189 if (secondValue.IsInt()) {
1190 auto handlerInfo = static_cast<uint32_t>(secondValue.GetInt());
1191 if (HandlerBase::IsNormalElement(handlerInfo) || HandlerBase::IsStringElement(handlerInfo)) {
1192 AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, hclass, hclass,
1193 OnHeapMode::NONE, HandlerBase::IsStoreOutOfBounds(handlerInfo));
1194 return;
1195 }
1196
1197 if (HandlerBase::IsTypedArrayElement(handlerInfo)) {
1198 OnHeapMode onHeap = HandlerBase::IsOnHeap(handlerInfo) ? OnHeapMode::ON_HEAP : OnHeapMode::NOT_ON_HEAP;
1199 AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, onHeap,
1200 HandlerBase::IsStoreOutOfBounds(handlerInfo));
1201 return;
1202 }
1203
1204 AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, hclass);
1205 } else if (secondValue.IsTransitionHandler()) {
1206 auto transitionHandler = TransitionHandler::Cast(secondValue.GetTaggedObject());
1207 auto transitionHClassVal = transitionHandler->GetTransitionHClass();
1208
1209 auto handlerInfoValue = transitionHandler->GetHandlerInfo();
1210 ASSERT(handlerInfoValue.IsInt());
1211 auto handlerInfo = static_cast<uint32_t>(handlerInfoValue.GetInt());
1212 if (transitionHClassVal.IsJSHClass()) {
1213 auto transitionHClass = JSHClass::Cast(transitionHClassVal.GetTaggedObject());
1214 if (HandlerBase::IsElement(handlerInfo)) {
1215 AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, hclass, transitionHClass,
1216 OnHeapMode::NONE, HandlerBase::IsStoreOutOfBounds(handlerInfo));
1217 return;
1218 }
1219 AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, transitionHClass);
1220 }
1221 } else if (secondValue.IsTransWithProtoHandler()) {
1222 auto transWithProtoHandler = TransWithProtoHandler::Cast(secondValue.GetTaggedObject());
1223 auto transitionHClassVal = transWithProtoHandler->GetTransitionHClass();
1224
1225 auto handlerInfoValue = transWithProtoHandler->GetHandlerInfo();
1226 ASSERT(handlerInfoValue.IsInt());
1227 auto handlerInfo = static_cast<uint32_t>(handlerInfoValue.GetInt());
1228 if (transitionHClassVal.IsJSHClass()) {
1229 auto transitionHClass = JSHClass::Cast(transitionHClassVal.GetTaggedObject());
1230 if (HandlerBase::IsElement(handlerInfo)) {
1231 AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, hclass, transitionHClass,
1232 OnHeapMode::NONE, HandlerBase::IsStoreOutOfBounds(handlerInfo));
1233 return;
1234 }
1235 AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, transitionHClass);
1236 }
1237 } else if (secondValue.IsPrototypeHandler()) {
1238 PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(secondValue.GetTaggedObject());
1239 auto cellValue = prototypeHandler->GetProtoCell();
1240 if (!cellValue.IsProtoChangeMarker()) {
1241 return;
1242 }
1243 ASSERT(cellValue.IsProtoChangeMarker());
1244 ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetTaggedObject());
1245 if (cell->GetHasChanged()) {
1246 return;
1247 }
1248 JSTaggedValue handlerInfoValue = prototypeHandler->GetHandlerInfo();
1249 ASSERT(handlerInfoValue.IsInt());
1250 auto handlerInfo = static_cast<uint32_t>(handlerInfoValue.GetInt());
1251 if (HandlerBase::IsElement(handlerInfo)) {
1252 AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, hclass, hclass,
1253 OnHeapMode::NONE, HandlerBase::IsStoreOutOfBounds(handlerInfo));
1254 return;
1255 }
1256 auto holder = prototypeHandler->GetHolder();
1257 auto holderHClass = holder.GetTaggedObject()->GetClass();
1258 AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, holderHClass, holderHClass);
1259 }
1260 }
1261
TryDumpProtoTransitionType(JSHClass *hclass)1262 void PGOProfiler::TryDumpProtoTransitionType(JSHClass *hclass)
1263 {
1264 JSHClass *ihc1 = JSHClass::FindRootHClass(hclass);
1265 auto transitionType = GetProfileType(ihc1, true);
1266 if (!transitionType.IsRootType() || !transitionType.IsTransitionClassType()) {
1267 return;
1268 }
1269 JSTaggedValue phc1Root = JSHClass::FindProtoRootHClass(ihc1);
1270 auto transitionProtoType = GetProfileType(JSHClass::Cast(phc1Root.GetTaggedObject()), true);
1271 if (!transitionProtoType.IsRootType()) {
1272 LOG_ECMA(DEBUG) << "Set as the prototype of a function again after transition happened for this prototype!";
1273 return;
1274 }
1275
1276 auto thread = vm_->GetJSThread();
1277 auto *transitionTable = thread->GetCurrentEcmaContext()->GetFunctionProtoTransitionTable();
1278 JSTaggedType ihc0 = transitionTable->GetFakeParent(JSTaggedType(ihc1));
1279 JSTaggedType baseIhc = transitionTable->GetFakeParent(phc1Root.GetRawData());
1280 if ((ihc0 == 0) || (baseIhc == 0)) {
1281 return;
1282 }
1283
1284 auto ihc0Obj = JSHClass::Cast(JSTaggedValue(ihc0).GetTaggedObject());
1285 auto baseIhcObj = JSHClass::Cast(JSTaggedValue(baseIhc).GetTaggedObject());
1286 UpdateLayout(ihc0Obj);
1287 UpdateLayout(ihc1);
1288 UpdateLayout(baseIhcObj);
1289
1290 auto ihc0RootType = GetProfileType(ihc0Obj);
1291 ASSERT(ihc0RootType.IsRootType());
1292 auto baseRootHClass = JSHClass::FindRootHClass(baseIhcObj);
1293 auto baseRootType = GetProfileType(baseRootHClass, true);
1294 if (!baseRootType.IsRootType()) {
1295 LOG_ECMA(DEBUG) << "Unsupported prototypes which cannot be recorded!";
1296 return;
1297 }
1298 auto baseType = GetProfileType(baseIhcObj);
1299 ASSERT(!baseType.IsNone());
1300 PGOProtoTransitionType protoTransitionType(ihc0RootType);
1301 protoTransitionType.SetBaseType(baseRootType, baseType);
1302 protoTransitionType.SetTransitionType(transitionType);
1303 protoTransitionType.SetTransitionProtoPt(transitionProtoType);
1304
1305 recordInfos_->GetProtoTransitionPool()->Add(protoTransitionType);
1306 }
1307
DumpOpType(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, ProfileTypeInfo *profileTypeInfo)1308 void PGOProfiler::DumpOpType(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset,
1309 uint32_t slotId, ProfileTypeInfo *profileTypeInfo)
1310 {
1311 JSTaggedValue slotValue = profileTypeInfo->Get(slotId);
1312 if (slotValue.IsInt()) {
1313 auto type = slotValue.GetInt();
1314 ProfileType recordType = GetRecordProfileType(abcId, recordName);
1315 recordInfos_->AddType(recordType, methodId, bcOffset, PGOSampleType(type));
1316 }
1317 }
1318
FunctionKindVerify(const JSFunction *ctorFunction)1319 bool PGOProfiler::FunctionKindVerify(const JSFunction *ctorFunction)
1320 {
1321 FunctionKind kind = Method::Cast(ctorFunction->GetMethod())->GetFunctionKind();
1322 return kind == FunctionKind::BASE_CONSTRUCTOR ||
1323 kind == FunctionKind::CLASS_CONSTRUCTOR ||
1324 kind == FunctionKind::DERIVED_CONSTRUCTOR;
1325 }
1326
DumpDefineClass(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, ProfileTypeInfo *profileTypeInfo)1327 void PGOProfiler::DumpDefineClass(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset,
1328 uint32_t slotId, ProfileTypeInfo *profileTypeInfo)
1329 {
1330 JSTaggedValue slotValue = profileTypeInfo->Get(slotId);
1331 if (!slotValue.IsProfileTypeInfoCell0()) {
1332 return;
1333 }
1334 JSTaggedValue handle = ProfileTypeInfoCell::Cast(slotValue)->GetHandle();
1335 if (!handle.IsHeapObject() || !handle.IsWeak()) {
1336 return;
1337 }
1338 auto object = handle.GetWeakReferentUnChecked();
1339 if (object->GetClass()->IsJSFunction()) {
1340 JSFunction *ctorFunction = JSFunction::Cast(object);
1341 auto ctorMethod = ctorFunction->GetMethod();
1342 if (!ctorMethod.IsMethod() || !FunctionKindVerify(ctorFunction)) {
1343 return;
1344 }
1345 ApEntityId ctorAbcId = GetMethodAbcId(ctorFunction);
1346 auto ctorJSMethod = Method::Cast(ctorMethod);
1347 auto ctorMethodId = ctorJSMethod->GetMethodId().GetOffset();
1348
1349 auto localType = ProfileType(ctorAbcId, ctorMethodId, ProfileType::Kind::ClassId, true);
1350 if (IsSkippableObjectTypeSafe(localType)) {
1351 return;
1352 }
1353 PGODefineOpType objDefType(localType);
1354 auto protoOrHClass = ctorFunction->GetProtoOrHClass();
1355 if (protoOrHClass.IsJSHClass()) {
1356 auto ihc = JSHClass::Cast(protoOrHClass.GetTaggedObject());
1357 SetRootProfileType(ihc, ctorAbcId, ctorMethodId, ProfileType::Kind::ClassId);
1358 recordInfos_->AddRootLayout(JSTaggedType(ihc), localType);
1359 protoOrHClass = ihc->GetProto();
1360 }
1361
1362 auto ctorRootHClass = JSHClass::FindRootHClass(ctorFunction->GetJSHClass());
1363 auto ctorType = GetProfileType(ctorRootHClass);
1364 if (!ctorType.IsRootType()) {
1365 LOG_ECMA(DEBUG) << "The profileType of constructor root hclass was not found.";
1366 } else {
1367 objDefType.SetCtorPt(ctorType);
1368 recordInfos_->AddRootLayout(JSTaggedType(ctorRootHClass), ctorType);
1369 }
1370
1371 if (protoOrHClass.IsJSObject()) {
1372 auto prototypeHClass = JSObject::Cast(protoOrHClass)->GetClass();
1373 auto prototypeRootHClass = JSHClass::FindRootHClass(prototypeHClass);
1374 ProfileType prototypeType = GetProfileType(prototypeRootHClass);
1375 if (!prototypeType.IsRootType()) {
1376 LOG_ECMA(DEBUG) << "The profileType of prototype root hclass was not found.";
1377 } else {
1378 objDefType.SetProtoTypePt(prototypeType);
1379 recordInfos_->AddRootLayout(JSTaggedType(prototypeRootHClass), prototypeType);
1380 }
1381 }
1382
1383 ProfileType recordType = GetRecordProfileType(abcId, recordName);
1384 recordInfos_->AddDefine(recordType, methodId, bcOffset, objDefType);
1385 }
1386 }
1387
DumpCreateObject(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, ProfileTypeInfo *profileTypeInfo, int32_t traceId)1388 void PGOProfiler::DumpCreateObject(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset,
1389 uint32_t slotId, ProfileTypeInfo *profileTypeInfo, int32_t traceId)
1390 {
1391 JSTaggedValue slotValue = profileTypeInfo->Get(slotId);
1392 if (!slotValue.IsHeapObject()) {
1393 return;
1394 }
1395 ProfileType recordType = GetRecordProfileType(abcId, recordName);
1396 if (slotValue.IsWeak()) {
1397 auto object = slotValue.GetWeakReferentUnChecked();
1398 if (object->GetClass()->IsHClass()) {
1399 auto newHClass = JSHClass::Cast(object);
1400 auto rootHClass = JSHClass::FindRootHClass(newHClass);
1401 ProfileType profileType = GetProfileType(rootHClass);
1402 if (!profileType.IsRootType()) {
1403 return;
1404 }
1405 ASSERT(profileType.GetKind() == ProfileType::Kind::ObjectLiteralId);
1406 PGOSampleType currentType(profileType);
1407 PGODefineOpType objDefType(profileType);
1408 recordInfos_->AddDefine(recordType, methodId, bcOffset, objDefType);
1409 recordInfos_->AddRootLayout(JSTaggedType(rootHClass), profileType);
1410 }
1411 } else if (slotValue.IsTrackInfoObject()) {
1412 auto currentType = PGOSampleType::CreateProfileType(abcId, traceId, ProfileType::Kind::ArrayLiteralId, true);
1413 auto profileType = currentType.GetProfileType();
1414 PGODefineOpType objDefType(profileType);
1415 TrackInfo *trackInfo = TrackInfo::Cast(slotValue.GetTaggedObject());
1416 auto elementsKind = trackInfo->GetElementsKind();
1417 objDefType.SetElementsKind(elementsKind);
1418 objDefType.SetElementsLength(trackInfo->GetArrayLength());
1419 objDefType.SetSpaceFlag(trackInfo->GetSpaceFlag());
1420 recordInfos_->AddDefine(recordType, methodId, bcOffset, objDefType);
1421 auto cachedHClass = trackInfo->GetCachedHClass();
1422 if (cachedHClass.IsJSHClass()) {
1423 auto hclass = JSHClass::Cast(cachedHClass.GetTaggedObject());
1424 recordInfos_->AddRootLayout(JSTaggedType(hclass), profileType);
1425 }
1426 }
1427 }
1428
DumpCall(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, ProfileTypeInfo *profileTypeInfo)1429 void PGOProfiler::DumpCall(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset,
1430 uint32_t slotId, ProfileTypeInfo *profileTypeInfo)
1431 {
1432 JSTaggedValue slotValue = profileTypeInfo->Get(slotId);
1433 ProfileType::Kind kind;
1434 int calleeMethodId = 0;
1435 ApEntityId calleeAbcId = 0;
1436 if (slotValue.IsInt()) {
1437 calleeMethodId = slotValue.GetInt();
1438 calleeAbcId = abcId;
1439 ASSERT(calleeMethodId <= 0);
1440 if (calleeMethodId == 0) {
1441 kind = ProfileType::Kind::MethodId;
1442 } else {
1443 kind = ProfileType::Kind::BuiltinFunctionId;
1444 }
1445 } else if (slotValue.IsJSFunction()) {
1446 JSFunction *callee = JSFunction::Cast(slotValue);
1447 Method *calleeMethod = Method::Cast(callee->GetMethod());
1448 calleeMethodId = static_cast<int>(calleeMethod->GetMethodId().GetOffset());
1449 calleeAbcId = GetMethodAbcId(callee->GetMethod());
1450 kind = ProfileType::Kind::MethodId;
1451 } else {
1452 return;
1453 }
1454 PGOSampleType type = PGOSampleType::CreateProfileType(calleeAbcId, std::abs(calleeMethodId), kind);
1455 ProfileType recordType = GetRecordProfileType(abcId, recordName);
1456 recordInfos_->AddCallTargetType(recordType, methodId, bcOffset, type);
1457 }
1458
DumpGetIterator(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, ProfileTypeInfo *profileTypeInfo)1459 void PGOProfiler::DumpGetIterator(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset,
1460 uint32_t slotId, ProfileTypeInfo *profileTypeInfo)
1461 {
1462 if (vm_->GetJSThread()->GetEnableLazyBuiltins()) {
1463 return;
1464 }
1465 JSTaggedValue value = profileTypeInfo->Get(slotId);
1466 if (!value.IsInt()) {
1467 return;
1468 }
1469 int iterKind = value.GetInt();
1470 ASSERT(iterKind <= 0);
1471 ProfileType::Kind pgoKind = ProfileType::Kind::BuiltinFunctionId;
1472 PGOSampleType type = PGOSampleType::CreateProfileType(abcId, std::abs(iterKind), pgoKind);
1473 ProfileType recordType = GetRecordProfileType(abcId, recordName);
1474 recordInfos_->AddCallTargetType(recordType, methodId, bcOffset, type);
1475 }
1476
DumpNewObjRange(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, ProfileTypeInfo *profileTypeInfo)1477 void PGOProfiler::DumpNewObjRange(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset,
1478 uint32_t slotId, ProfileTypeInfo *profileTypeInfo)
1479 {
1480 JSTaggedValue slotValue = profileTypeInfo->Get(slotId);
1481 int ctorMethodId = 0;
1482 if (slotValue.IsInt()) {
1483 ctorMethodId = slotValue.GetInt();
1484 } else if (slotValue.IsJSFunction()) {
1485 JSFunction *callee = JSFunction::Cast(slotValue);
1486 Method *calleeMethod = Method::Cast(callee->GetMethod());
1487 ctorMethodId = static_cast<int>(calleeMethod->GetMethodId().GetOffset());
1488 } else {
1489 return;
1490 }
1491 PGOSampleType type;
1492 if (ctorMethodId > 0) {
1493 type = PGOSampleType::CreateProfileType(abcId, ctorMethodId, ProfileType::Kind::ClassId, true);
1494 } else {
1495 auto kind = ProfileType::Kind::BuiltinFunctionId;
1496 type = PGOSampleType::CreateProfileType(abcId, std::abs(ctorMethodId), kind);
1497 }
1498 ProfileType recordType = GetRecordProfileType(abcId, recordName);
1499 recordInfos_->AddCallTargetType(recordType, methodId, bcOffset, type);
1500 }
1501
DumpInstanceof(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, ProfileTypeInfo *profileTypeInfo)1502 void PGOProfiler::DumpInstanceof(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset,
1503 uint32_t slotId, ProfileTypeInfo *profileTypeInfo)
1504 {
1505 JSTaggedValue firstValue = profileTypeInfo->Get(slotId);
1506 if (!firstValue.IsHeapObject()) {
1507 if (firstValue.IsHole()) {
1508 // Mega state
1509 AddObjectInfoWithMega(abcId, recordName, methodId, bcOffset);
1510 }
1511 return;
1512 }
1513 if (firstValue.IsWeak()) {
1514 TaggedObject *object = firstValue.GetWeakReferentUnChecked();
1515 if (object->GetClass()->IsHClass()) {
1516 JSHClass *hclass = JSHClass::Cast(object);
1517 // Since pgo does not support symbol, we choose to return if hclass having @@hasInstance
1518 JSHandle<GlobalEnv> env = vm_->GetGlobalEnv();
1519 JSTaggedValue key = env->GetHasInstanceSymbol().GetTaggedValue();
1520 JSHClass *functionPrototypeHC = JSObject::Cast(env->GetFunctionPrototype().GetTaggedValue())->GetClass();
1521 JSTaggedValue foundHClass = TryFindKeyInPrototypeChain(object, hclass, key);
1522 if (!foundHClass.IsUndefined() && JSHClass::Cast(foundHClass.GetTaggedObject()) != functionPrototypeHC) {
1523 return;
1524 }
1525 AddObjectInfo(abcId, recordName, methodId, bcOffset, hclass, hclass, hclass);
1526 }
1527 return;
1528 }
1529 // Poly Not Consider now
1530 return;
1531 }
1532
UpdateLayout(JSHClass *hclass)1533 void PGOProfiler::UpdateLayout(JSHClass *hclass)
1534 {
1535 auto parentHClass = hclass->GetParent();
1536 if (!GetProfileType(hclass).IsRootType() && parentHClass.IsJSHClass()) {
1537 UpdateTransitionLayout(JSHClass::Cast(parentHClass.GetTaggedObject()), hclass);
1538 } else {
1539 auto rootHClass = JSHClass::FindRootHClass(hclass);
1540 ProfileType rootType = GetProfileType(rootHClass, true);
1541 if (!rootType.IsRootType()) {
1542 return;
1543 }
1544
1545 auto prototypeHClass = JSHClass::FindProtoRootHClass(rootHClass);
1546 if (prototypeHClass.IsJSHClass()) {
1547 auto prototypeObject = JSHClass::Cast(prototypeHClass.GetTaggedObject());
1548 ProfileType prototypeType = GetProfileType(prototypeObject, true);
1549 if (prototypeType.IsRootType()) {
1550 recordInfos_->AddRootPtType(rootType, prototypeType);
1551 UpdateLayout(JSHClass::Cast(prototypeHClass.GetTaggedObject()));
1552 }
1553 }
1554
1555 auto curType = GetOrInsertProfileType(hclass, rootType);
1556 recordInfos_->UpdateLayout(rootType, JSTaggedType(hclass), curType);
1557 }
1558 }
1559
UpdateTransitionLayout(JSHClass* parent, JSHClass* child)1560 void PGOProfiler::UpdateTransitionLayout(JSHClass* parent, JSHClass* child)
1561 {
1562 auto rootHClass = JSHClass::FindRootHClass(parent);
1563 auto rootType = GetProfileType(rootHClass, true);
1564 if (!rootType.IsRootType()) {
1565 return;
1566 }
1567 // If the child hclass is set as a prototype, it will become the root hclass. Need to give up.
1568 if (GetProfileType(child).IsRootType()) {
1569 return;
1570 }
1571 CStack<JSHClass *> hclassVec;
1572 hclassVec.emplace(child);
1573 hclassVec.emplace(parent);
1574
1575 while (!GetProfileType(parent).IsRootType()) {
1576 auto parentHCValue = parent->GetParent();
1577 if (!parentHCValue.IsJSHClass()) {
1578 break;
1579 }
1580 parent = JSHClass::Cast(parentHCValue.GetTaggedObject());
1581 hclassVec.emplace(parent);
1582 }
1583
1584 auto prototypeRootHClassVal = JSHClass::FindProtoRootHClass(rootHClass);
1585 if (prototypeRootHClassVal.IsJSHClass()) {
1586 auto prototypeRootHClass = JSHClass::Cast(prototypeRootHClassVal.GetTaggedObject());
1587 auto prototypeType = GetProfileType(prototypeRootHClass);
1588 if (prototypeType.IsRootType()) {
1589 recordInfos_->AddRootPtType(rootType, prototypeType);
1590 UpdateLayout(prototypeRootHClass);
1591 }
1592 }
1593
1594 parent = hclassVec.top();
1595 hclassVec.pop();
1596 auto parentType = GetProfileType(parent);
1597 while (!hclassVec.empty()) {
1598 child = hclassVec.top();
1599 hclassVec.pop();
1600 auto childType = GetOrInsertProfileType(child, rootType);
1601 recordInfos_->UpdateTransitionLayout(
1602 rootType, JSTaggedType(parent), parentType, JSTaggedType(child), childType);
1603 parentType = childType;
1604 parent = child;
1605 }
1606 }
1607
AddTransitionObjectInfo(ProfileType recordType, EntityId methodId, int32_t bcOffset, JSHClass* receiver, JSHClass* hold, JSHClass* holdTra, PGOSampleType accessorMethod)1608 bool PGOProfiler::AddTransitionObjectInfo(ProfileType recordType,
1609 EntityId methodId,
1610 int32_t bcOffset,
1611 JSHClass* receiver,
1612 JSHClass* hold,
1613 JSHClass* holdTra,
1614 PGOSampleType accessorMethod)
1615 {
1616 auto receiverRootType = FindRootProfileType(receiver);
1617 if (!receiverRootType.IsRootType()) {
1618 return false;
1619 }
1620
1621 auto holdRootType = FindRootProfileType(hold);
1622 if (!holdRootType.IsRootType()) {
1623 return true;
1624 }
1625
1626 auto receiverType = GetOrInsertProfileType(receiver, receiverRootType);
1627 auto holdType = GetOrInsertProfileType(hold, holdRootType);
1628 auto holdTraType = GetOrInsertProfileType(holdTra, holdRootType);
1629
1630 if (receiver != hold) {
1631 UpdateLayout(receiver);
1632 }
1633
1634 if (holdType == holdTraType) {
1635 UpdateLayout(hold);
1636 } else {
1637 UpdateTransitionLayout(hold, holdTra);
1638 }
1639
1640 PGOObjectInfo info(receiverRootType, receiverType, holdRootType, holdType, holdRootType, holdTraType,
1641 accessorMethod);
1642 UpdatePrototypeChainInfo(receiver, hold, info);
1643 recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info);
1644 return true;
1645 }
1646
AddObjectInfo(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, JSHClass *receiver, JSHClass *hold, JSHClass *holdTra, uint32_t accessorMethodId)1647 bool PGOProfiler::AddObjectInfo(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset,
1648 JSHClass *receiver, JSHClass *hold, JSHClass *holdTra, uint32_t accessorMethodId)
1649 {
1650 PGOSampleType accessor = PGOSampleType::CreateProfileType(abcId, accessorMethodId, ProfileType::Kind::MethodId);
1651 ProfileType recordType = GetRecordProfileType(abcId, recordName);
1652 return AddTransitionObjectInfo(recordType, methodId, bcOffset, receiver, hold, holdTra, accessor);
1653 }
1654
UpdatePrototypeChainInfo(JSHClass *receiver, JSHClass *holder, PGOObjectInfo &info)1655 void PGOProfiler::UpdatePrototypeChainInfo(JSHClass *receiver, JSHClass *holder, PGOObjectInfo &info)
1656 {
1657 if (receiver == holder) {
1658 return;
1659 }
1660
1661 std::vector<std::pair<ProfileType, ProfileType>> protoChain;
1662 JSTaggedValue proto = JSHClass::FindProtoHClass(receiver);
1663 while (proto.IsJSHClass()) {
1664 auto protoHClass = JSHClass::Cast(proto.GetTaggedObject());
1665 if (protoHClass == holder) {
1666 break;
1667 }
1668 auto protoRootType = FindRootProfileType(protoHClass);
1669 if (!protoRootType.IsRootType()) {
1670 break;
1671 }
1672 auto protoType = GetOrInsertProfileType(protoHClass, protoRootType);
1673 protoChain.emplace_back(protoRootType, protoType);
1674 proto = JSHClass::FindProtoHClass(protoHClass);
1675 }
1676 if (!protoChain.empty()) {
1677 info.AddPrototypePt(protoChain);
1678 }
1679 }
1680
AddObjectInfoWithMega( ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset)1681 void PGOProfiler::AddObjectInfoWithMega(
1682 ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset)
1683 {
1684 auto megaType = ProfileType::CreateMegaType();
1685 PGOObjectInfo info(megaType, megaType, megaType, megaType, megaType, megaType, PGOSampleType());
1686 ProfileType recordType = GetRecordProfileType(abcId, recordName);
1687 recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info);
1688 }
1689
AddBuiltinsInfoByNameInInstance(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, JSHClass *receiver)1690 bool PGOProfiler::AddBuiltinsInfoByNameInInstance(ApEntityId abcId, const CString &recordName, EntityId methodId,
1691 int32_t bcOffset, JSHClass *receiver)
1692 {
1693 auto thread = vm_->GetJSThread();
1694 auto type = receiver->GetObjectType();
1695 const auto &ctorEntries = thread->GetCtorHclassEntries();
1696 auto entry = ctorEntries.find(receiver);
1697 if (entry != ctorEntries.end()) {
1698 AddBuiltinsGlobalInfo(abcId, recordName, methodId, bcOffset, entry->second);
1699 return true;
1700 }
1701
1702 auto builtinsId = ToBuiltinsTypeId(type);
1703 if (!builtinsId.has_value()) {
1704 return false;
1705 }
1706 JSHClass *exceptRecvHClass = nullptr;
1707 if (builtinsId == BuiltinTypeId::ARRAY) {
1708 bool receiverIsPrototype = receiver->IsPrototype();
1709 exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind(), receiverIsPrototype);
1710 } else if (builtinsId == BuiltinTypeId::STRING) {
1711 exceptRecvHClass = receiver;
1712 } else {
1713 exceptRecvHClass = thread->GetBuiltinInstanceHClass(builtinsId.value());
1714 }
1715
1716 if (exceptRecvHClass != receiver) {
1717 // When JSType cannot uniquely identify builtins object, it is necessary to
1718 // query the receiver on the global constants.
1719 if (builtinsId == BuiltinTypeId::OBJECT) {
1720 exceptRecvHClass = JSHClass::Cast(thread->GlobalConstants()->GetIteratorResultClass().GetTaggedObject());
1721 if (exceptRecvHClass == receiver) {
1722 GlobalIndex globalsId;
1723 globalsId.UpdateGlobalConstId(static_cast<size_t>(ConstantIndex::ITERATOR_RESULT_CLASS));
1724 AddBuiltinsGlobalInfo(abcId, recordName, methodId, bcOffset, globalsId);
1725 return true;
1726 }
1727 }
1728 return false;
1729 }
1730
1731 return AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, receiver, receiver);
1732 }
1733
AddBuiltinsInfoByNameInProt(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, JSHClass *receiver, JSHClass *hold)1734 bool PGOProfiler::AddBuiltinsInfoByNameInProt(ApEntityId abcId, const CString &recordName, EntityId methodId,
1735 int32_t bcOffset, JSHClass *receiver, JSHClass *hold)
1736 {
1737 auto type = receiver->GetObjectType();
1738 auto builtinsId = ToBuiltinsTypeId(type);
1739 if (!builtinsId.has_value()) {
1740 return false;
1741 }
1742 auto thread = vm_->GetJSThread();
1743 JSHClass *exceptRecvHClass = nullptr;
1744 if (builtinsId == BuiltinTypeId::ARRAY) {
1745 bool receiverIsPrototype = receiver->IsPrototype();
1746 exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind(), receiverIsPrototype);
1747 } else if (builtinsId == BuiltinTypeId::STRING) {
1748 exceptRecvHClass = receiver;
1749 } else {
1750 exceptRecvHClass = thread->GetBuiltinInstanceHClass(builtinsId.value());
1751 }
1752
1753 auto exceptHoldHClass = thread->GetBuiltinPrototypeHClass(builtinsId.value());
1754 auto exceptPrototypeOfPrototypeHClass =
1755 thread->GetBuiltinPrototypeOfPrototypeHClass(builtinsId.value());
1756 // iterator needs to find two layers of prototype
1757 if (builtinsId == BuiltinTypeId::ARRAY_ITERATOR) {
1758 if ((exceptRecvHClass != receiver) ||
1759 (exceptHoldHClass != hold && exceptPrototypeOfPrototypeHClass != hold)) {
1760 return false;
1761 }
1762 } else if (IsTypedArrayType(builtinsId.value())) {
1763 auto exceptRecvHClassOnHeap = thread->GetBuiltinExtraHClass(builtinsId.value());
1764 ASSERT_PRINT(exceptRecvHClassOnHeap == nullptr || exceptRecvHClassOnHeap->IsOnHeapFromBitField(),
1765 "must be on heap");
1766 if (IsJSHClassNotEqual(receiver, hold, exceptRecvHClass, exceptRecvHClassOnHeap,
1767 exceptHoldHClass, exceptPrototypeOfPrototypeHClass)) {
1768 return false;
1769 }
1770 } else if (exceptRecvHClass != receiver || exceptHoldHClass != hold) {
1771 return false;
1772 }
1773
1774 return AddBuiltinsInfo(abcId, recordName, methodId, bcOffset, receiver, receiver);
1775 }
1776
IsJSHClassNotEqual(JSHClass *receiver, JSHClass *hold, JSHClass *exceptRecvHClass, JSHClass *exceptRecvHClassOnHeap, JSHClass *exceptHoldHClass, JSHClass *exceptPrototypeOfPrototypeHClass)1777 bool PGOProfiler::IsJSHClassNotEqual(JSHClass *receiver, JSHClass *hold, JSHClass *exceptRecvHClass,
1778 JSHClass *exceptRecvHClassOnHeap, JSHClass *exceptHoldHClass,
1779 JSHClass *exceptPrototypeOfPrototypeHClass)
1780 {
1781 //exceptRecvHClass = IHC, exceptRecvHClassOnHeap = IHC OnHeap
1782 //exceptHoldHClass = PHC, exceptPrototypeOfPrototypeHClass = HClass of X.prototype.prototype
1783 return ((exceptRecvHClass != receiver && exceptRecvHClassOnHeap != receiver) ||
1784 (exceptHoldHClass != hold && exceptPrototypeOfPrototypeHClass != hold));
1785 }
1786
CheckProtoChangeMarker(JSTaggedValue cellValue) const1787 bool PGOProfiler::CheckProtoChangeMarker(JSTaggedValue cellValue) const
1788 {
1789 if (!cellValue.IsProtoChangeMarker()) {
1790 return true;
1791 }
1792 ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetTaggedObject());
1793 return cell->GetHasChanged();
1794 }
1795
AddBuiltinsGlobalInfo(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, GlobalIndex globalsId)1796 void PGOProfiler::AddBuiltinsGlobalInfo(ApEntityId abcId, const CString &recordName, EntityId methodId,
1797 int32_t bcOffset, GlobalIndex globalsId)
1798 {
1799 ProfileType recordType = GetRecordProfileType(abcId, recordName);
1800 PGOObjectInfo info(ProfileType::CreateGlobals(abcId, globalsId));
1801 recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info);
1802 }
1803
AddBuiltinsInfo( ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, JSHClass *receiver, JSHClass *transitionHClass, OnHeapMode onHeap, bool everOutOfBounds)1804 bool PGOProfiler::AddBuiltinsInfo(
1805 ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, JSHClass *receiver,
1806 JSHClass *transitionHClass, OnHeapMode onHeap, bool everOutOfBounds)
1807 {
1808 ProfileType recordType = GetRecordProfileType(abcId, recordName);
1809 if (receiver->IsJSArray()) {
1810 auto type = receiver->GetObjectType();
1811 auto elementsKind = receiver->GetElementsKind();
1812 auto transitionElementsKind = transitionHClass->GetElementsKind();
1813 auto profileType = ProfileType::CreateBuiltinsArray(abcId, type, elementsKind, transitionElementsKind,
1814 everOutOfBounds);
1815 PGOObjectInfo info(profileType);
1816 recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info);
1817 } else if (receiver->IsTypedArray()) {
1818 JSType jsType = receiver->GetObjectType();
1819 auto profileType = ProfileType::CreateBuiltinsTypedArray(abcId, jsType, onHeap, everOutOfBounds);
1820 PGOObjectInfo info(profileType);
1821 recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info);
1822 } else {
1823 auto type = receiver->GetObjectType();
1824 PGOObjectInfo info(ProfileType::CreateBuiltins(abcId, type));
1825 recordInfos_->AddObjectInfo(recordType, methodId, bcOffset, info);
1826 }
1827 return true;
1828 }
1829
IsRecoredTransRootType(ProfileType type)1830 bool PGOProfiler::IsRecoredTransRootType(ProfileType type)
1831 {
1832 if (!type.IsRootType() || !type.IsTransitionType()) {
1833 return false;
1834 }
1835 if (std::find(recordedTransRootType_.begin(), recordedTransRootType_.end(), type) != recordedTransRootType_.end()) {
1836 LOG_ECMA(DEBUG) << "forbide to add more than 1 hclass for a root type!";
1837 return true;
1838 }
1839 recordedTransRootType_.emplace_back(type);
1840 return false;
1841 }
1842
SetRootProfileType(JSHClass *root, ApEntityId abcId, uint32_t type, ProfileType::Kind kind)1843 void PGOProfiler::SetRootProfileType(JSHClass *root, ApEntityId abcId, uint32_t type, ProfileType::Kind kind)
1844 {
1845 ProfileType traceType(root->GetProfileType());
1846 if (traceType.IsNone()) {
1847 traceType = ProfileType(abcId, type, kind, true);
1848 if (IsRecoredTransRootType(traceType)) {
1849 return;
1850 }
1851 root->SetProfileType(traceType.GetRaw());
1852 }
1853 }
1854
FindRootProfileType(JSHClass *hclass)1855 ProfileType PGOProfiler::FindRootProfileType(JSHClass *hclass)
1856 {
1857 auto rootHClass = JSHClass::FindRootHClass(hclass);
1858 return GetProfileType(rootHClass, true);
1859 }
1860
GetOrInsertProfileType(JSHClass *child, ProfileType rootType)1861 ProfileType PGOProfiler::GetOrInsertProfileType(JSHClass *child, ProfileType rootType)
1862 {
1863 ProfileType childType = GetProfileType(child);
1864 if (childType.IsNone()) {
1865 ASSERT(rootType.IsRootType());
1866 childType = PGOTypeGenerator::GenerateProfileType(JSTaggedType(child), rootType);
1867 child->SetProfileType(childType.GetRaw());
1868 }
1869 return childType;
1870 }
1871
GetProfileType(JSHClass *hclass, bool check)1872 ProfileType PGOProfiler::GetProfileType(JSHClass *hclass, bool check)
1873 {
1874 auto result = ProfileType(hclass->GetProfileType());
1875 if (check) {
1876 if (IsSkippableObjectTypeSafe(result)) {
1877 result = ProfileType::PROFILE_TYPE_NONE;
1878 }
1879 }
1880 return result;
1881 }
1882
ProcessReferences(const WeakRootVisitor &visitor)1883 void PGOProfiler::ProcessReferences(const WeakRootVisitor &visitor)
1884 {
1885 if (!isEnable_) {
1886 return;
1887 }
1888 preDumpWorkList_.Iterate([this, &visitor](WorkNode *node) {
1889 auto object = reinterpret_cast<TaggedObject *>(node->GetValue());
1890 auto fwd = visitor(object);
1891 if (fwd == nullptr) {
1892 preDumpWorkList_.Remove(node);
1893 nativeAreaAllocator_->Delete(node);
1894 return;
1895 }
1896 if (fwd != object) {
1897 node->SetValue(JSTaggedType(fwd));
1898 }
1899 });
1900 }
1901
Iterate(const RootVisitor &visitor)1902 void PGOProfiler::Iterate(const RootVisitor &visitor)
1903 {
1904 if (!isEnable_) {
1905 return;
1906 }
1907 // If the IC of the method is stable, the current design forces the dump data.
1908 // Must pause dump during GC.
1909 dumpWorkList_.Iterate([&visitor](WorkNode* node) {
1910 visitor(Root::ROOT_VM, ObjectSlot(node->GetValueAddr()));
1911 });
1912 }
1913
PGOProfiler(EcmaVM* vm, bool isEnable)1914 PGOProfiler::PGOProfiler(EcmaVM* vm, bool isEnable)
1915 : nativeAreaAllocator_(std::make_unique<NativeAreaAllocator>()), vm_(vm), isEnable_(isEnable)
1916 {
1917 if (isEnable_) {
1918 recordInfos_ = std::make_unique<PGORecordDetailInfos>(0);
1919 }
1920 };
1921
~PGOProfiler()1922 PGOProfiler::~PGOProfiler()
1923 {
1924 Reset(false);
1925 }
1926
Reset(bool isEnable)1927 void PGOProfiler::Reset(bool isEnable)
1928 {
1929 LockHolder lock(recordInfoMutex_);
1930 isEnable_ = isEnable;
1931 methodCount_ = 0;
1932 if (recordInfos_) {
1933 recordInfos_->Clear();
1934 } else {
1935 if (isEnable_) {
1936 recordInfos_ = std::make_unique<PGORecordDetailInfos>(0);
1937 }
1938 }
1939 }
1940
GetMethodAbcId(JSTaggedValue jsMethod)1941 ApEntityId PGOProfiler::GetMethodAbcId(JSTaggedValue jsMethod)
1942 {
1943 ASSERT(jsMethod.IsMethod());
1944 CString pfName;
1945
1946 const auto *pf = Method::Cast(jsMethod)->GetJSPandaFile();
1947 if (pf != nullptr) {
1948 pfName = pf->GetJSPandaFileDesc();
1949 }
1950 ApEntityId abcId(0);
1951 if (!PGOProfilerManager::GetInstance()->GetPandaFileId(pfName, abcId) && !pfName.empty()) {
1952 LOG_ECMA(ERROR) << "Get method abc id failed. abcName: " << pfName;
1953 }
1954 return abcId;
1955 }
GetMethodAbcId(JSFunction *jsFunction)1956 ApEntityId PGOProfiler::GetMethodAbcId(JSFunction *jsFunction)
1957 {
1958 CString pfName;
1959 auto jsMethod = jsFunction->GetMethod();
1960 if (jsMethod.IsMethod()) {
1961 return GetMethodAbcId(jsMethod);
1962 }
1963 LOG_ECMA(ERROR) << "Get method abc id failed. Not a method.";
1964 UNREACHABLE();
1965 }
1966
GetRecordProfileType(JSFunction *jsFunction, const CString &recordName)1967 ProfileType PGOProfiler::GetRecordProfileType(JSFunction *jsFunction, const CString &recordName)
1968 {
1969 CString pfName;
1970 auto jsMethod = jsFunction->GetMethod();
1971 if (jsMethod.IsMethod()) {
1972 const auto *pf = Method::Cast(jsMethod)->GetJSPandaFile();
1973 if (pf != nullptr) {
1974 pfName = pf->GetJSPandaFileDesc();
1975 }
1976 }
1977 const auto &pf = JSPandaFileManager::GetInstance()->FindJSPandaFile(pfName);
1978 if (pf == nullptr) {
1979 LOG_ECMA(ERROR) << "Get record profile type failed. pf is null, pfName: " << pfName
1980 << ", recordName: " << recordName;
1981 return ProfileType::PROFILE_TYPE_NONE;
1982 }
1983 return GetRecordProfileType(pf, GetMethodAbcId(jsFunction), recordName);
1984 }
1985
GetRecordProfileType(ApEntityId abcId, const CString &recordName)1986 ProfileType PGOProfiler::GetRecordProfileType(ApEntityId abcId, const CString &recordName)
1987 {
1988 CString pfDesc;
1989 PGOProfilerManager::GetInstance()->GetPandaFileDesc(abcId, pfDesc);
1990 const auto &pf = JSPandaFileManager::GetInstance()->FindJSPandaFile(pfDesc);
1991 if (pf == nullptr) {
1992 LOG_ECMA(ERROR) << "Get record profile type failed. pf is null, pfDesc: " << pfDesc
1993 << ", recordName: " << recordName;
1994 return ProfileType::PROFILE_TYPE_NONE;
1995 }
1996 return GetRecordProfileType(pf, abcId, recordName);
1997 }
1998
GetRecordProfileType(const std::shared_ptr<JSPandaFile> &pf, ApEntityId abcId, const CString &recordName)1999 ProfileType PGOProfiler::GetRecordProfileType(const std::shared_ptr<JSPandaFile> &pf, ApEntityId abcId,
2000 const CString &recordName)
2001 {
2002 ASSERT(pf != nullptr);
2003 JSRecordInfo *recordInfo = nullptr;
2004 bool hasRecord = pf->CheckAndGetRecordInfo(recordName, &recordInfo);
2005 if (!hasRecord) {
2006 LOG_ECMA(ERROR) << "Get recordInfo failed. recordName: " << recordName;
2007 return ProfileType::PROFILE_TYPE_NONE;
2008 }
2009 ProfileType recordType {0};
2010 if (pf->IsBundlePack()) {
2011 recordType = CreateRecordProfileType(abcId, ProfileType::RECORD_ID_FOR_BUNDLE);
2012 recordInfos_->GetRecordPool()->Add(recordType, recordName);
2013 return recordType;
2014 }
2015 if (recordInfo->classId != JSPandaFile::CLASSID_OFFSET_NOT_FOUND) {
2016 recordType = CreateRecordProfileType(abcId, recordInfo->classId);
2017 recordInfos_->GetRecordPool()->Add(recordType, recordName);
2018 return recordType;
2019 }
2020 LOG_ECMA(ERROR) << "Invalid classId, skip it. recordName: " << recordName << ", isCjs: " << recordInfo->isCjs
2021 << ", isJson: " << recordInfo->isJson;
2022 return ProfileType::PROFILE_TYPE_NONE;
2023 }
2024
PushBack(WorkNode *node)2025 void PGOProfiler::WorkList::PushBack(WorkNode *node)
2026 {
2027 if (node == nullptr) {
2028 LOG_ECMA(FATAL) << "PGOProfiler::WorkList::PushBack:node is nullptr";
2029 UNREACHABLE();
2030 }
2031 if (last_ == nullptr) {
2032 first_ = node;
2033 last_ = node;
2034 } else {
2035 last_->SetNext(node);
2036 node->SetPrev(last_);
2037 last_ = node;
2038 }
2039 node->SetWorkList(this);
2040 }
2041
PopFront()2042 PGOProfiler::WorkNode *PGOProfiler::WorkList::PopFront()
2043 {
2044 WorkNode *result = nullptr;
2045 if (first_ != nullptr) {
2046 result = first_;
2047 if (first_->GetNext() != nullptr) {
2048 first_ = first_->GetNext();
2049 first_->SetPrev(nullptr);
2050 } else {
2051 first_ = nullptr;
2052 last_ = nullptr;
2053 }
2054 result->SetNext(nullptr);
2055 result->SetWorkList(nullptr);
2056 }
2057 return result;
2058 }
2059
Remove(WorkNode *node)2060 void PGOProfiler::WorkList::Remove(WorkNode *node)
2061 {
2062 if (node->GetPrev() != nullptr) {
2063 node->GetPrev()->SetNext(node->GetNext());
2064 }
2065 if (node->GetNext() != nullptr) {
2066 node->GetNext()->SetPrev(node->GetPrev());
2067 }
2068 if (node == first_) {
2069 first_ = node->GetNext();
2070 }
2071 if (node == last_) {
2072 last_ = node->GetPrev();
2073 }
2074 node->SetPrev(nullptr);
2075 node->SetNext(nullptr);
2076 node->SetWorkList(nullptr);
2077 }
2078
Iterate(Callback callback) const2079 void PGOProfiler::WorkList::Iterate(Callback callback) const
2080 {
2081 auto current = first_;
2082 while (current != nullptr) {
2083 auto next = current->GetNext();
2084 callback(current);
2085 current = next;
2086 }
2087 }
2088
CreateRecordProfileType(ApEntityId abcId, ApEntityId classId)2089 ProfileType PGOProfiler::CreateRecordProfileType(ApEntityId abcId, ApEntityId classId)
2090 {
2091 return {abcId, classId, ProfileType::Kind::RecordClassId};
2092 }
2093
TryFindKeyInPrototypeChain(TaggedObject *currObj, JSHClass *currHC, JSTaggedValue key)2094 JSTaggedValue PGOProfiler::TryFindKeyInPrototypeChain(TaggedObject *currObj, JSHClass *currHC, JSTaggedValue key)
2095 {
2096 // This is a temporary solution for Instanceof Only!
2097 // Do NOT use this function for other purpose.
2098 if (currHC->IsDictionaryMode()) {
2099 return JSTaggedValue(currHC);
2100 }
2101 while (!JSTaggedValue(currHC).IsUndefinedOrNull()) {
2102 if (LIKELY(!currHC->IsDictionaryMode())) {
2103 int entry = JSHClass::FindPropertyEntry(vm_->GetJSThread(), currHC, key);
2104 if (entry != -1) {
2105 return JSTaggedValue(currHC);
2106 }
2107 } else {
2108 TaggedArray *array = TaggedArray::Cast(JSObject::Cast(currObj)->GetProperties().GetTaggedObject());
2109 ASSERT(array->IsDictionaryMode());
2110 NameDictionary *dict = NameDictionary::Cast(array);
2111 int entry = dict->FindEntry(key);
2112 if (entry != -1) {
2113 return JSTaggedValue(currHC);
2114 }
2115 }
2116 currObj = currHC->GetProto().GetTaggedObject();
2117 if (JSTaggedValue(currObj).IsUndefinedOrNull()) {
2118 break;
2119 }
2120 currHC = currObj->GetClass();
2121 }
2122 return JSTaggedValue::Undefined();
2123 }
InitJITProfiler()2124 void PGOProfiler::InitJITProfiler()
2125 {
2126 jitProfiler_ = new JITProfiler(vm_);
2127 jitProfiler_->InitJITProfiler();
2128 }
2129
2130 } // namespace panda::ecmascript::pgo
2131