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/js_tagged_value.h" 25 #include "ecmascript/jspandafile/method_literal.h" 26 #include "ecmascript/mem/c_containers.h" 27 #include "ecmascript/mem/native_area_allocator.h" 28 #include "ecmascript/mem/region.h" 29 #include "ecmascript/mem/visitor.h" 30 #include "ecmascript/pgo_profiler/types/pgo_profiler_type.h" 31 #include "ecmascript/pgo_profiler/types/pgo_type_generator.h" 32 #include "ecmascript/platform/mutex.h" 33 #include "ecmascript/taskpool/task.h" 34 #include "ecmascript/pgo_profiler/pgo_utils.h" 35 #include "ecmascript/pgo_profiler/types/pgo_profile_type.h" 36 37 namespace panda::ecmascript { 38 class ProfileTypeInfo; 39 class JSFunction; 40 41 namespace pgo { 42 class PGORecordDetailInfos; 43 44 enum class SampleMode : uint8_t { 45 HOTNESS_MODE, 46 CALL_MODE, 47 }; 48 49 class PGOProfiler { 50 public: 51 NO_COPY_SEMANTIC(PGOProfiler); 52 NO_MOVE_SEMANTIC(PGOProfiler); 53 54 PGOProfiler(EcmaVM *vm, bool isEnable); 55 56 virtual ~PGOProfiler(); 57 58 static ProfileType CreateRecordProfileType(ApEntityId abcId, ApEntityId classId); 59 void ProfileCreateObject(JSTaggedType object, ApEntityId abcId, int32_t traceId); 60 void ProfileDefineClass(JSTaggedType ctor); 61 void ProfileDefineGetterSetter( 62 JSHClass *receverHClass, JSHClass *holderHClass, const JSHandle<JSTaggedValue> &func, int32_t pcOffset); 63 void ProfileClassRootHClass(JSTaggedType ctor, JSTaggedType rootHcValue, 64 ProfileType::Kind kind = ProfileType::Kind::ClassId); 65 void UpdateRootProfileType(JSHClass *oldHClass, JSHClass *newHClass); 66 SetSaveTimestamp(std::chrono::system_clock::time_point timestamp)67 void SetSaveTimestamp(std::chrono::system_clock::time_point timestamp) 68 { 69 saveTimestamp_ = timestamp; 70 } 71 72 void PGOPreDump(JSTaggedType func); 73 void PGODump(JSTaggedType func); 74 75 void WaitPGODumpPause(); 76 void WaitPGODumpResume(); 77 void WaitPGODumpFinish(); 78 79 void HandlePGOPreDump(); 80 void HandlePGODump(bool force); 81 82 void ProcessReferences(const WeakRootVisitor &visitor); 83 void Iterate(const RootVisitor &visitor); 84 85 void UpdateTrackArrayLength(JSTaggedValue trackInfoVal, uint32_t newSize); 86 void UpdateTrackSpaceFlag(TaggedObject *object, RegionSpaceFlag spaceFlag); 87 void UpdateTrackElementsKind(JSTaggedValue trackInfoVal, ElementsKind newKind); 88 void UpdateTrackInfo(JSTaggedValue trackInfoVal); 89 90 JSTaggedValue TryFindKeyInPrototypeChain(TaggedObject *currObj, JSHClass *currHC, JSTaggedValue key); 91 92 private: 93 static constexpr uint32_t MERGED_EVERY_COUNT = 50; 94 static constexpr uint32_t MS_PRE_SECOND = 1000; 95 enum class BCType : uint8_t { 96 STORE, 97 LOAD, 98 }; 99 100 enum class State : uint8_t { 101 STOP, 102 PAUSE, 103 START, 104 FORCE_SAVE, 105 FORCE_SAVE_PAUSE, 106 }; 107 108 void ProfileBytecode(ApEntityId abcId, const CString &recordName, JSTaggedValue value); 109 bool PausePGODump(); 110 111 void DumpICByName(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, 112 ProfileTypeInfo *profileTypeInfo, BCType type); 113 void DumpICByValue(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 114 uint32_t slotId, ProfileTypeInfo *profileTypeInfo, BCType type); 115 116 void DumpICByNameWithPoly(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 117 JSTaggedValue cacheValue, BCType type); 118 void DumpICByValueWithPoly(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 119 JSTaggedValue cacheValue, BCType type); 120 121 void DumpICByNameWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 122 JSHClass *hclass, JSTaggedValue secondValue, BCType type); 123 void DumpICByValueWithHandler(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 124 JSHClass *hclass, JSTaggedValue secondValue, BCType type); 125 void DumpByForce(); 126 127 void DumpOpType(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, 128 ProfileTypeInfo *profileTypeInfo); 129 void DumpDefineClass(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 130 uint32_t slotId, ProfileTypeInfo *profileTypeInfo); 131 bool FunctionKindVerify(const JSFunction *ctorFunction); 132 void DumpCreateObject(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 133 uint32_t slotId, ProfileTypeInfo *profileTypeInfo, int32_t traceId); 134 void DumpCall(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, uint32_t slotId, 135 ProfileTypeInfo *profileTypeInfo); 136 void DumpNewObjRange(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 137 uint32_t slotId, ProfileTypeInfo *profileTypeInfo); 138 void DumpGetIterator(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 139 uint32_t slotId, ProfileTypeInfo *profileTypeInfo); 140 void DumpInstanceof(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 141 uint32_t slotId, ProfileTypeInfo *profileTypeInfo); 142 143 void UpdateLayout(JSHClass *hclass); 144 void UpdateTranstionLayout(JSHClass *parent, JSHClass *child); 145 void AddTranstionObjectInfo(ProfileType recordType, EntityId methodId, int32_t bcOffset, JSHClass *receiver, 146 JSHClass *hold, JSHClass *holdTra); 147 void UpdatePrototypeChainInfo(JSHClass *receiver, JSHClass *holder, PGOObjectInfo &info); 148 149 void AddObjectInfo(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, 150 JSHClass *receiver, JSHClass *hold, JSHClass *holdTra); 151 void AddObjectInfoWithMega(ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset); 152 void AddBuiltinsInfo( 153 ApEntityId abcId, const CString &recordName, EntityId methodId, int32_t bcOffset, JSHClass *receiver, 154 JSHClass *transitionHClass, OnHeapMode onHeap = OnHeapMode::NONE); 155 156 ProfileType GetProfileType(JSTaggedType root, JSTaggedType child); 157 ProfileType GetOrInsertProfileType(JSTaggedType root, JSTaggedType child); 158 void InsertProfileType(JSTaggedType root, JSTaggedType child, ProfileType traceType); 159 160 class WorkNode; 161 void UpdateExtraProfileTypeInfo(ApEntityId abcId, const CString& recordName, EntityId methodId, WorkNode* current); 162 WorkNode* PopFromProfileQueue(); 163 void SaveProfiler(bool force); 164 165 class PGOProfilerTask : public Task { 166 public: PGOProfilerTask(PGOProfiler * profiler,int32_t id)167 explicit PGOProfilerTask(PGOProfiler *profiler, int32_t id) 168 : Task(id), profiler_(profiler){}; 169 virtual ~PGOProfilerTask() override = default; 170 Run(uint32_t threadIndex)171 bool Run([[maybe_unused]] uint32_t threadIndex) override 172 { 173 profiler_->HandlePGODump(profiler_->isForce_); 174 return true; 175 } 176 177 NO_COPY_SEMANTIC(PGOProfilerTask); 178 NO_MOVE_SEMANTIC(PGOProfilerTask); 179 private: 180 PGOProfiler *profiler_; 181 }; 182 183 using PcOffset = int32_t; 184 class ExtraProfileTypeInfo { 185 public: GetReceiverAddr()186 uintptr_t GetReceiverAddr() const 187 { 188 return reinterpret_cast<uintptr_t>(&receiver); 189 } 190 GetHolderAddr()191 uintptr_t GetHolderAddr() const 192 { 193 return reinterpret_cast<uintptr_t>(&holder); 194 } 195 GetReceiverHClass()196 JSHClass* GetReceiverHClass() const 197 { 198 return receiver.GetTaggedObject()->GetClass(); 199 } 200 GetHolderHClass()201 JSHClass* GetHolderHClass() const 202 { 203 return holder.GetTaggedObject()->GetClass(); 204 } 205 SetReceiver(JSHClass * hclass)206 void SetReceiver(JSHClass* hclass) 207 { 208 receiver = JSTaggedValue::Cast(hclass); 209 } 210 SetHolder(JSHClass * hclass)211 void SetHolder(JSHClass* hclass) 212 { 213 holder = JSTaggedValue::Cast(hclass); 214 } 215 SetReceiver(JSTaggedValue value)216 void SetReceiver(JSTaggedValue value) 217 { 218 receiver = value; 219 } 220 SetHolder(JSTaggedValue value)221 void SetHolder(JSTaggedValue value) 222 { 223 holder = value; 224 } 225 SetReceiver(TaggedObject * value)226 void SetReceiver(TaggedObject* value) 227 { 228 receiver = JSTaggedValue::Cast(value); 229 } 230 SetHolder(TaggedObject * value)231 void SetHolder(TaggedObject* value) 232 { 233 holder = JSTaggedValue::Cast(value); 234 } 235 GetReceiver()236 JSTaggedValue GetReceiver() const 237 { 238 return receiver; 239 } 240 GetHolder()241 JSTaggedValue GetHolder() const 242 { 243 return holder; 244 } 245 Clear()246 void Clear() 247 { 248 receiver = JSTaggedValue::Hole(); 249 holder = JSTaggedValue::Hole(); 250 } 251 ClearReceiver()252 void ClearReceiver() 253 { 254 receiver = JSTaggedValue::Hole(); 255 } 256 ClearHolder()257 void ClearHolder() 258 { 259 holder = JSTaggedValue::Hole(); 260 } 261 262 bool operator==(const ExtraProfileTypeInfo& other) const 263 { 264 return receiver == other.receiver && holder == other.holder; 265 } 266 IsHole()267 bool IsHole() const 268 { 269 return receiver.IsHole() && holder.IsHole(); 270 } 271 ProcessReceiver(const WeakRootVisitor & visitor)272 void ProcessReceiver(const WeakRootVisitor& visitor) 273 { 274 if (!receiver.IsHeapObject()) { 275 return; 276 } 277 auto obj = receiver.GetTaggedObject(); 278 auto fwd = visitor(obj); 279 if (fwd == nullptr) { 280 ClearReceiver(); 281 } else if (fwd != obj) { 282 SetReceiver(fwd); 283 } 284 } 285 ProcessHolder(const WeakRootVisitor & visitor)286 void ProcessHolder(const WeakRootVisitor& visitor) 287 { 288 if (!holder.IsHeapObject()) { 289 return; 290 } 291 auto obj = holder.GetTaggedObject(); 292 auto fwd = visitor(obj); 293 if (fwd == nullptr) { 294 ClearHolder(); 295 } else if (fwd != obj) { 296 SetHolder(fwd); 297 } 298 } 299 300 private: 301 JSTaggedValue receiver {JSTaggedValue::Hole()}; 302 JSTaggedValue holder {JSTaggedValue::Hole()}; 303 }; 304 305 class WorkList; 306 class WorkNode { 307 public: WorkNode(JSTaggedType value)308 WorkNode(JSTaggedType value) : value_(value) {} SetPrev(WorkNode * prev)309 void SetPrev(WorkNode *prev) 310 { 311 prev_ = prev; 312 } 313 SetNext(WorkNode * next)314 void SetNext(WorkNode *next) 315 { 316 next_ = next; 317 } 318 SetValue(JSTaggedType value)319 void SetValue(JSTaggedType value) 320 { 321 value_ = value; 322 } 323 SetWorkList(WorkList * workList)324 void SetWorkList(WorkList *workList) 325 { 326 workList_ = workList; 327 } 328 GetPrev()329 WorkNode *GetPrev() const 330 { 331 return prev_; 332 } 333 GetNext()334 WorkNode *GetNext() const 335 { 336 return next_; 337 } 338 GetValue()339 JSTaggedType GetValue() const 340 { 341 return value_; 342 } 343 GetValueAddr()344 uintptr_t GetValueAddr() const 345 { 346 return reinterpret_cast<uintptr_t>(&value_); 347 } 348 GetWorkList()349 WorkList *GetWorkList() const 350 { 351 return workList_; 352 } 353 SetExtraProfileTypeInfo(int32_t pcOffset,JSHClass * receiverHClass,JSHClass * holderHClass)354 void SetExtraProfileTypeInfo(int32_t pcOffset, JSHClass* receiverHClass, JSHClass* holderHClass) 355 { 356 auto it = map_.find(pcOffset); 357 358 ExtraProfileTypeInfo info; 359 info.SetReceiver(receiverHClass); 360 info.SetHolder(holderHClass); 361 362 if (it == map_.end()) { 363 map_.insert(std::make_pair(pcOffset, info)); 364 return; 365 } 366 367 if (it->second == info) { 368 return; 369 } 370 371 it->second.Clear(); 372 } 373 HasExtraProfileTypeInfo()374 bool HasExtraProfileTypeInfo() 375 { 376 return !map_.empty(); 377 } 378 ProcessExtraProfileTypeInfo(const WeakRootVisitor & visitor)379 void ProcessExtraProfileTypeInfo(const WeakRootVisitor& visitor) 380 { 381 for (auto& iter: map_) { 382 auto& info = iter.second; 383 info.ProcessReceiver(visitor); 384 info.ProcessHolder(visitor); 385 } 386 } 387 IterateExtraProfileTypeInfo(const RootVisitor & visitor)388 void IterateExtraProfileTypeInfo(const RootVisitor& visitor) 389 { 390 if (HasExtraProfileTypeInfo()) { 391 for (auto& iter: map_) { 392 auto& info = iter.second; 393 visitor(Root::ROOT_VM, ObjectSlot(info.GetReceiverAddr())); 394 visitor(Root::ROOT_VM, ObjectSlot(info.GetHolderAddr())); 395 } 396 } 397 } 398 GetExtraProfileTypeInfo()399 std::unordered_map<PcOffset, ExtraProfileTypeInfo>& GetExtraProfileTypeInfo() 400 { 401 return map_; 402 } 403 404 private: 405 WorkList *workList_ { nullptr }; 406 WorkNode *prev_ { nullptr }; 407 WorkNode *next_ { nullptr }; 408 JSTaggedType value_ { JSTaggedValue::Undefined().GetRawData() }; 409 std::unordered_map<PcOffset, ExtraProfileTypeInfo> map_; 410 }; 411 412 class WorkList { 413 public: 414 using Callback = std::function<void(WorkNode *node)>; IsEmpty()415 bool IsEmpty() const 416 { 417 return first_ == nullptr; 418 } 419 void PushBack(WorkNode *node); 420 WorkNode *PopFront(); 421 void Remove(WorkNode *node); 422 void Iterate(Callback callback) const; 423 private: 424 WorkNode *first_ { nullptr }; 425 WorkNode *last_ { nullptr }; 426 }; 427 428 static ApEntityId GetMethodAbcId(JSFunction *jsFunction); 429 ProfileType GetRecordProfileType(JSFunction *jsFunction, const CString &recordName); 430 ProfileType GetRecordProfileType(ApEntityId abcId, const CString &recordName); 431 ProfileType GetRecordProfileType(const std::shared_ptr<JSPandaFile> &pf, ApEntityId abcId, 432 const CString &recordName); 433 434 void Reset(bool isEnable); 435 436 EcmaVM *vm_ { nullptr }; 437 bool isEnable_ { false }; 438 bool isForce_ {false}; 439 State state_ { State::STOP }; 440 uint32_t methodCount_ { 0 }; 441 std::chrono::system_clock::time_point saveTimestamp_; 442 Mutex mutex_; 443 ConditionVariable condition_; 444 WorkList dumpWorkList_; 445 WorkList preDumpWorkList_; 446 CMap<JSTaggedType, PGOTypeGenerator *> tracedProfiles_; 447 std::unique_ptr<PGORecordDetailInfos> recordInfos_; 448 friend class PGOProfilerManager; 449 }; 450 } // namespace pgo 451 } // namespace panda::ecmascript 452 #endif // ECMASCRIPT_PGO_PROFILER_H 453