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