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 #ifndef ECMASCRIPT_PGO_PROFILER_H 17 #define ECMASCRIPT_PGO_PROFILER_H 18 19 #include <chrono> 20 #include <memory> 21 22 #include "common_components/taskpool/task.h" 23 #include "ecmascript/common.h" 24 #include "ecmascript/elements.h" 25 #include "ecmascript/global_index.h" 26 #include "ecmascript/js_handle.h" 27 #include "ecmascript/js_tagged_value.h" 28 #include "ecmascript/js_thread.h" 29 #include "ecmascript/jspandafile/method_literal.h" 30 #include "ecmascript/mem/c_containers.h" 31 #include "ecmascript/mem/native_area_allocator.h" 32 #include "ecmascript/mem/region.h" 33 #include "ecmascript/mem/visitor.h" 34 #include "ecmascript/pgo_profiler/pgo_extra_profiler.h" 35 #include "ecmascript/pgo_profiler/pgo_state.h" 36 #include "ecmascript/pgo_profiler/pgo_utils.h" 37 #include "ecmascript/pgo_profiler/types/pgo_profile_type.h" 38 #include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" 39 #include "ecmascript/pgo_profiler/types/pgo_type_generator.h" 40 #include "ecmascript/platform/mutex.h" 41 42 namespace panda::ecmascript { 43 class ProfileTypeInfo; 44 class JSFunction; 45 class GlobalIndex; 46 class JITProfiler; 47 48 namespace pgo { 49 class PGORecordDetailInfos; 50 class PGOProfilerManager; 51 using State = pgo::PGOState::State; 52 53 enum class SampleMode : uint8_t { 54 HOTNESS_MODE, 55 CALL_MODE, 56 }; 57 58 class PGOProfiler { 59 public: 60 PGOProfiler(EcmaVM* vm, bool isEnable); 61 virtual ~PGOProfiler(); 62 NO_COPY_SEMANTIC(PGOProfiler); 63 NO_MOVE_SEMANTIC(PGOProfiler); 64 void PUBLIC_API RecordProfileType(JSHClass* hclass, JSPandaFile* pandaFile, int32_t traceId); 65 static ProfileType CreateRecordProfileType(ApEntityId abcId, ApEntityId classId); 66 void ProfileDefineClass(JSTaggedType ctor); 67 void ProfileProtoTransitionClass(JSHandle<JSFunction> func, 68 JSHandle<JSHClass> hclass, 69 JSHandle<JSTaggedValue> proto); 70 void ProfileProtoTransitionPrototype(JSHandle<JSFunction> func, 71 JSHandle<JSTaggedValue> prototype, 72 JSHandle<JSTaggedValue> oldPrototype, 73 JSHandle<JSTaggedValue> baseIhc); 74 void ProfileDefineGetterSetter(JSHClass* receverHClass, 75 JSHClass* holderHClass, 76 const JSHandle<JSTaggedValue>& func, 77 int32_t pcOffset); 78 void ProfileClassRootHClass(JSTaggedType ctor, 79 JSTaggedType rootHcValue, 80 ProfileType::Kind kind = ProfileType::Kind::ClassId); 81 void PUBLIC_API ProfileNapiRootHClass(JSTaggedType ctor, 82 JSTaggedType rootHcValue, 83 ProfileType::Kind kind = ProfileType::Kind::NapiId); 84 void UpdateRootProfileTypeSafe(JSHClass* oldHClass, JSHClass* newHClass); 85 void InitJITProfiler(); 86 void PGOPreDump(JSTaggedType func); 87 void PGODump(JSTaggedType func); 88 void SuspendByGC(); 89 void ResumeByGC(); 90 void HandlePGOPreDump(); 91 void HandlePGODump(); 92 void ProcessReferences(const WeakRootVisitor& visitor); 93 void Iterate(RootVisitor& visitor); 94 void IteratePGOPreFuncList(RootVisitor& visitor) const; 95 void UpdateTrackArrayLength(JSTaggedValue trackInfoVal, uint32_t newSize); 96 void UpdateTrackSpaceFlag(TaggedObject* object, RegionSpaceFlag spaceFlag); 97 void UpdateTrackInfo(JSTaggedValue trackInfoVal); 98 JSTaggedValue TryFindKeyInPrototypeChain(TaggedObject* currObj, JSHClass* currHC, JSTaggedValue key); 99 static ApEntityId PUBLIC_API GetMethodAbcId(const JSThread *thread, JSFunction* jsFunction); 100 static ApEntityId PUBLIC_API GetMethodAbcId(const JSThread *thread, JSTaggedValue jsMethod); 101 void Reset(bool isEnable); 102 void InsertSkipCtorMethodIdSafe(EntityId ctorMethodId); 103 void SetSaveTimestamp(std::chrono::system_clock::time_point timestamp); 104 JITProfiler* PUBLIC_API GetJITProfile(); 105 void SetStopAndNotify(); 106 bool SetStartIfStop(); 107 void TrySave(); 108 void DumpBeforeDestroy(JSThread *thread); 109 110 private: 111 class WorkNode; 112 class WorkList; 113 114 enum class BCType : uint8_t { 115 STORE, 116 LOAD, 117 }; 118 119 void ProfileBytecode(ApEntityId abcId, const CString& recordName, JSTaggedValue funcValue); 120 void DumpICByName(ApEntityId abcId, 121 const CString& recordName, 122 EntityId methodId, 123 int32_t bcOffset, 124 uint32_t slotId, 125 ProfileTypeInfo* profileTypeInfo, 126 BCType type); 127 void DumpICByValue(ApEntityId abcId, 128 const CString& recordName, 129 EntityId methodId, 130 int32_t bcOffset, 131 uint32_t slotId, 132 ProfileTypeInfo* profileTypeInfo, 133 BCType type); 134 void DumpICByNameWithPoly(ApEntityId abcId, 135 const CString& recordName, 136 EntityId methodId, 137 int32_t bcOffset, 138 JSTaggedValue cacheValue, 139 BCType type); 140 void DumpICByValueWithPoly(ApEntityId abcId, 141 const CString& recordName, 142 EntityId methodId, 143 int32_t bcOffset, 144 JSTaggedValue cacheValue, 145 BCType type); 146 bool DumpICByNameWithHandler(ApEntityId abcId, 147 const CString& recordName, 148 EntityId methodId, 149 int32_t bcOffset, 150 JSHClass* hclass, 151 JSTaggedValue secondValue, 152 BCType type); 153 bool DumpICLoadByNameWithHandler(ApEntityId abcId, 154 const CString& recordName, 155 EntityId methodId, 156 int32_t bcOffset, 157 JSHClass* hclass, 158 JSTaggedValue secondValue); 159 void DumpICByValueWithHandler(ApEntityId abcId, 160 const CString& recordName, 161 EntityId methodId, 162 int32_t bcOffset, 163 JSHClass* hclass, 164 JSTaggedValue secondValue, 165 BCType type); 166 void TryDumpProtoTransitionType(JSHClass* hclass); 167 void DumpOpType(ApEntityId abcId, 168 const CString& recordName, 169 EntityId methodId, 170 int32_t bcOffset, 171 uint32_t slotId, 172 ProfileTypeInfo* profileTypeInfo); 173 void DumpDefineClass(ApEntityId abcId, 174 const CString& recordName, 175 EntityId methodId, 176 int32_t bcOffset, 177 uint32_t slotId, 178 ProfileTypeInfo* profileTypeInfo); 179 bool FunctionKindVerify(const JSFunction* ctorFunction); 180 void DumpCreateObject(ApEntityId abcId, 181 const CString& recordName, 182 EntityId methodId, 183 int32_t bcOffset, 184 uint32_t slotId, 185 ProfileTypeInfo* profileTypeInfo, 186 int32_t traceId); 187 void DumpCall(ApEntityId abcId, 188 const CString& recordName, 189 EntityId methodId, 190 int32_t bcOffset, 191 uint32_t slotId, 192 ProfileTypeInfo* profileTypeInfo); 193 void DumpNewObjRange(ApEntityId abcId, 194 const CString& recordName, 195 EntityId methodId, 196 int32_t bcOffset, 197 uint32_t slotId, 198 ProfileTypeInfo* profileTypeInfo); 199 void DumpGetIterator(ApEntityId abcId, 200 const CString& recordName, 201 EntityId methodId, 202 int32_t bcOffset, 203 uint32_t slotId, 204 ProfileTypeInfo* profileTypeInfo); 205 void DumpInstanceof(ApEntityId abcId, 206 const CString& recordName, 207 EntityId methodId, 208 int32_t bcOffset, 209 uint32_t slotId, 210 ProfileTypeInfo* profileTypeInfo); 211 void UpdateLayout(JSHClass* hclass); 212 void UpdateTransitionLayout(JSHClass* parent, JSHClass* child); 213 bool AddTransitionObjectInfo(ProfileType recordType, 214 EntityId methodId, 215 int32_t bcOffset, 216 JSHClass* receiver, 217 JSHClass* hold, 218 JSHClass* holdTra, 219 PGOSampleType accessorMethod); 220 void UpdatePrototypeChainInfo(JSHClass* receiver, JSHClass* holder, PGOObjectInfo& info); 221 bool AddObjectInfo(ApEntityId abcId, 222 const CString& recordName, 223 EntityId methodId, 224 int32_t bcOffset, 225 JSHClass* receiver, 226 JSHClass* hold, 227 JSHClass* holdTra, 228 uint32_t accessorMethodId = 0); 229 void AddObjectInfoWithMega(ApEntityId abcId, const CString& recordName, EntityId methodId, int32_t bcOffset); 230 bool AddBuiltinsInfoByNameInInstance( 231 ApEntityId abcId, const CString& recordName, EntityId methodId, int32_t bcOffset, JSHClass* receiver); 232 bool AddBuiltinsInfoByNameInProt(ApEntityId abcId, 233 const CString& recordName, 234 EntityId methodId, 235 int32_t bcOffset, 236 JSHClass* receiver, 237 JSHClass* hold); 238 bool IsTypedArrayRootHClass(JSType jsType, OnHeapMode mode, JSHClass *receiver); 239 bool AddBuiltinsInfo(ApEntityId abcId, 240 const CString& recordName, 241 EntityId methodId, 242 int32_t bcOffset, 243 JSHClass* receiver, 244 JSHClass* transitionHClass, 245 OnHeapMode onHeap = OnHeapMode::NONE, 246 bool everOutOfBounds = false); 247 void AddBuiltinsGlobalInfo( 248 ApEntityId abcId, const CString& recordName, EntityId methodId, int32_t bcOffset, GlobalIndex globalId); 249 void SetRootProfileType(JSHClass* root, ApEntityId abcId, uint32_t type, ProfileType::Kind kind); 250 ProfileType FindRootProfileType(JSHClass* hclass); 251 ProfileType GetOrInsertProfileType(JSHClass* child, ProfileType rootType); 252 ProfileType GetProfileType(JSHClass* hclass, bool check = false); 253 bool IsRecoredTransRootType(ProfileType type); 254 bool HasValidExtraProfileTypeInfo(JSFunction* func); 255 void ProcessExtraProfileTypeInfo(JSFunction* func, 256 ApEntityId abcId, 257 const CString& recordName, 258 JSTaggedValue methodValue, 259 WorkNode* current); 260 void UpdateExtraProfileTypeInfo(ApEntityId abcId, 261 const CString& recordName, 262 EntityId methodId, 263 WorkNode* current); 264 WorkNode* PopFromProfileQueue(); 265 bool IsJSHClassNotEqual(JSHClass* receiver, 266 JSHClass* hold, 267 JSHClass* exceptRecvHClass, 268 JSHClass* exceptRecvHClassOnHeap, 269 JSHClass* exceptHoldHClass, 270 JSHClass* exceptPrototypeOfPrototypeHClass); 271 bool CheckProtoChangeMarker(JSTaggedValue cellValue) const; 272 ProfileType GetRecordProfileType(JSFunction* jsFunction, const CString& recordName); 273 ProfileType GetRecordProfileType(ApEntityId abcId, const CString& recordName); 274 ProfileType GetRecordProfileType(const std::shared_ptr<JSPandaFile>& pf, 275 ApEntityId abcId, 276 const CString& recordName); 277 bool IsSkippableObjectTypeSafe(ProfileType type); 278 bool IsSkippableCtor(uint32_t entityId); 279 bool InsertDefinedCtor(uint32_t entityId); 280 281 inline void SetCurrentGlobalEnv(JSTaggedValue globalEnv); 282 inline JSHandle<GlobalEnv> GetCurrentGlobalEnv() const; 283 284 class WorkNode { 285 public: WorkNode(JSTaggedType value)286 WorkNode(JSTaggedType value): value_(value) {} SetPrev(WorkNode * prev)287 void SetPrev(WorkNode* prev) 288 { 289 prev_ = prev; 290 } 291 SetNext(WorkNode * next)292 void SetNext(WorkNode* next) 293 { 294 next_ = next; 295 } 296 SetValue(JSTaggedType value)297 void SetValue(JSTaggedType value) 298 { 299 value_ = value; 300 } 301 SetWorkList(WorkList * workList)302 void SetWorkList(WorkList* workList) 303 { 304 workList_ = workList; 305 } 306 GetPrev()307 WorkNode* GetPrev() const 308 { 309 return prev_; 310 } 311 GetNext()312 WorkNode* GetNext() const 313 { 314 return next_; 315 } 316 GetValue()317 JSTaggedType GetValue() const 318 { 319 return value_; 320 } 321 GetValueAddr()322 uintptr_t GetValueAddr() const 323 { 324 return reinterpret_cast<uintptr_t>(&value_); 325 } 326 GetWorkList()327 WorkList* GetWorkList() const 328 { 329 return workList_; 330 } 331 332 private: 333 WorkList* workList_ {nullptr}; 334 WorkNode* prev_ {nullptr}; 335 WorkNode* next_ {nullptr}; 336 JSTaggedType value_ {JSTaggedValue::Undefined().GetRawData()}; 337 }; 338 339 class WorkList { 340 public: 341 using Callback = std::function<void(WorkNode* node)>; 342 bool IsEmpty(); 343 void PushBack(WorkNode* node); 344 WorkNode* PopFront(); 345 void Remove(WorkNode* node); 346 void Iterate(Callback callback) const; 347 void Reset(); 348 349 private: 350 WorkNode* first_ {nullptr}; 351 WorkNode* last_ {nullptr}; 352 Mutex workListMutex_; 353 }; 354 355 class ToRunningScope { 356 public: ToRunningScope(ThreadHolder * holder,bool isEnable)357 ToRunningScope(ThreadHolder *holder, bool isEnable) : holder_(holder), scope_(nullptr) 358 { 359 if (isEnable) { 360 scope_ = new ThreadHolder::TryBindMutatorScope(holder); 361 holder->TransferToRunning(); 362 } 363 } 364 ~ToRunningScope()365 ~ToRunningScope() 366 { 367 if (scope_ != nullptr) { 368 holder_->TransferToNative(); 369 delete scope_; 370 } 371 } 372 373 private: 374 ThreadHolder *holder_; 375 ThreadHolder::TryBindMutatorScope *scope_; 376 }; 377 378 private: 379 static constexpr uint32_t MERGED_EVERY_COUNT {50}; 380 static constexpr uint32_t MS_PRE_SECOND {1000}; 381 JSTaggedValue globalEnv_ {JSTaggedValue::Hole()}; 382 std::unique_ptr<NativeAreaAllocator> nativeAreaAllocator_; 383 EcmaVM* vm_ {nullptr}; 384 bool isEnable_ {false}; 385 uint32_t methodCount_ {0}; 386 std::chrono::system_clock::time_point saveTimestamp_; 387 WorkList dumpWorkList_; 388 WorkList preDumpWorkList_; 389 std::shared_ptr<PGORecordDetailInfos> recordInfos_; 390 std::unique_ptr<PGOState> state_; 391 PGOProfilerManager* manager_ {nullptr}; 392 // AOT only supports executing Defineclass bc once currently. 393 // If defineclass executed multiple times, It will gives up collection. 394 CUnorderedSet<uint32_t> definedCtorMethodId_; 395 CUnorderedSet<uint32_t> skipCtorMethodId_; 396 Mutex skipCtorMethodIdMutex_; 397 JITProfiler* jitProfiler_ {nullptr}; 398 CVector<ProfileType> recordedTransRootType_; 399 ThreadHolder *holder_ {nullptr}; 400 friend class PGOProfilerManager; 401 }; 402 403 class PGODumpPauseScope { 404 public: PGODumpPauseScope(std::shared_ptr<PGOProfiler> profiler)405 explicit PGODumpPauseScope(std::shared_ptr<PGOProfiler> profiler): profiler_(profiler) 406 { 407 profiler_->SuspendByGC(); 408 } 409 ~PGODumpPauseScope()410 ~PGODumpPauseScope() 411 { 412 profiler_->ResumeByGC(); 413 } 414 415 NO_COPY_SEMANTIC(PGODumpPauseScope); 416 NO_MOVE_SEMANTIC(PGODumpPauseScope); 417 418 private: 419 std::shared_ptr<PGOProfiler> profiler_; 420 }; 421 } // namespace pgo 422 } // namespace panda::ecmascript 423 #endif // ECMASCRIPT_PGO_PROFILER_H 424