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