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