1 /** 2 * Copyright (c) 2021-2024 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 #ifndef PANDA_RUNTIME_STACK_WALKER_H 16 #define PANDA_RUNTIME_STACK_WALKER_H 17 18 #include <variant> 19 20 #include "runtime/include/cframe.h" 21 #include "runtime/include/cframe_iterators.h" 22 #include "runtime/include/coretypes/tagged_value.h" 23 #include "runtime/interpreter/frame.h" 24 #include "compiler/code_info/code_info.h" 25 26 namespace ark { 27 28 enum class UnwindPolicy { 29 ALL, // unwing all frames including inlined 30 SKIP_INLINED, // unwing all frames excluding inlined 31 ONLY_INLINED, // unwind all inlined frames within single cframe 32 }; 33 34 class FrameAccessor { 35 public: 36 using CFrameType = CFrame; 37 using FrameVariant = std::variant<Frame *, CFrame>; 38 FrameAccessor(const FrameVariant & frame)39 explicit FrameAccessor(const FrameVariant &frame) : frame_(frame) {} 40 IsValid()41 bool IsValid() const 42 { 43 return IsCFrame() || GetIFrame() != nullptr; 44 } 45 IsCFrame()46 bool IsCFrame() const 47 { 48 return std::holds_alternative<CFrameType>(frame_); 49 } 50 GetCFrame()51 CFrameType &GetCFrame() 52 { 53 ASSERT(IsCFrame()); 54 return std::get<CFrameType>(frame_); 55 } 56 GetCFrame()57 const CFrameType &GetCFrame() const 58 { 59 ASSERT(IsCFrame()); 60 return std::get<CFrameType>(frame_); 61 } 62 GetIFrame()63 Frame *GetIFrame() 64 { 65 return std::get<Frame *>(frame_); 66 } 67 GetIFrame()68 const Frame *GetIFrame() const 69 { 70 return std::get<Frame *>(frame_); 71 } 72 73 private: 74 FrameVariant frame_; 75 }; 76 77 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) 78 class StackWalker { 79 public: 80 static constexpr size_t CALLEE_REGS_COUNT = 81 GetCalleeRegsCount(RUNTIME_ARCH, false) + GetCalleeRegsCount(RUNTIME_ARCH, true); 82 83 using SlotType = std::conditional_t<ArchTraits<RUNTIME_ARCH>::IS_64_BITS, uint64_t, uint32_t>; 84 using FrameVariant = std::variant<Frame *, CFrame>; 85 using CFrameType = CFrame; 86 using CodeInfo = compiler::CodeInfo; 87 using VRegInfo = compiler::VRegInfo; 88 using CalleeRegsBuffer = std::array<SlotType, CALLEE_REGS_COUNT>; 89 90 // Use static method to be able to ASSERT(thread->IsRuntimeCallEnabled()) before StackWalker construction to avoid 91 // crash in constructor, e.g. while GetTopFrameFromFp() 92 static PANDA_PUBLIC_API StackWalker Create(const ManagedThread *thread, UnwindPolicy policy = UnwindPolicy::ALL); 93 94 StackWalker() = default; 95 StackWalker(void *fp, bool isFrameCompiled, uintptr_t npc, UnwindPolicy policy = UnwindPolicy::ALL); 96 97 virtual ~StackWalker() = default; 98 99 NO_COPY_SEMANTIC(StackWalker); 100 NO_MOVE_SEMANTIC(StackWalker); 101 102 void Reset(const ManagedThread *thread); 103 104 void Verify(); 105 106 PANDA_PUBLIC_API void NextFrame(); 107 108 PANDA_PUBLIC_API Method *GetMethod(); 109 GetMethod()110 const Method *GetMethod() const 111 { 112 return IsCFrame() ? GetCFrame().GetMethod() : GetIFrame()->GetMethod(); 113 } 114 GetBytecodePc()115 size_t GetBytecodePc() const 116 { 117 return IsCFrame() ? GetCFrameBytecodePc() : GetIFrame()->GetBytecodeOffset(); 118 } 119 GetNativePc()120 size_t GetNativePc() const 121 { 122 return IsCFrame() ? GetCFrameNativePc() : 0; 123 } 124 GetFp()125 void *GetFp() 126 { 127 return IsCFrame() ? reinterpret_cast<void *>(GetCFrame().GetFrameOrigin()) 128 : reinterpret_cast<void *>(GetIFrame()); 129 } 130 HasFrame()131 bool HasFrame() const 132 { 133 return IsCFrame() || GetIFrame() != nullptr; 134 } 135 136 template <typename Func> IterateObjects(Func func)137 bool IterateObjects(Func func) 138 { 139 return IterateRegs<true, false>(func); 140 } 141 142 template <typename Func> IterateObjectsWithInfo(Func func)143 bool IterateObjectsWithInfo(Func func) 144 { 145 return IterateRegs<true, true>(func); 146 } 147 148 template <typename Func> IterateVRegsWithInfo(Func func)149 bool IterateVRegsWithInfo(Func func) 150 { 151 return IterateRegs<false, true>(func); 152 } 153 IsCFrame()154 bool IsCFrame() const 155 { 156 return std::holds_alternative<CFrameType>(frame_); 157 } 158 159 interpreter::VRegister GetVRegValue(size_t vregNum); 160 161 template <bool IS_DYNAMIC = false, typename T> 162 void SetVRegValue(VRegInfo regInfo, T value); 163 GetCFrame()164 CFrameType &GetCFrame() 165 { 166 ASSERT(IsCFrame()); 167 return std::get<CFrameType>(frame_); 168 } 169 GetCFrame()170 const CFrameType &GetCFrame() const 171 { 172 ASSERT(IsCFrame()); 173 return std::get<CFrameType>(frame_); 174 } 175 GetIFrame()176 Frame *GetIFrame() 177 { 178 return std::get<Frame *>(frame_); 179 } 180 GetIFrame()181 const Frame *GetIFrame() const 182 { 183 return std::get<Frame *>(frame_); 184 } 185 GetCompiledCodeEntry()186 auto GetCompiledCodeEntry() const 187 { 188 ASSERT(IsCFrame()); 189 return codeInfo_.GetCode(); 190 } 191 192 Frame *ConvertToIFrame(FrameKind *prevFrameKind, uint32_t *numInlinedMethods); 193 194 bool IsCompilerBoundFrame(SlotType *prev); 195 196 FrameKind GetPreviousFrameKind() const; 197 198 FrameAccessor GetNextFrame(); 199 GetCurrentFrame()200 FrameAccessor GetCurrentFrame() 201 { 202 return FrameAccessor(frame_); 203 } 204 205 PANDA_PUBLIC_API bool IsDynamicMethod() const; 206 207 void DumpFrame(std::ostream &os); 208 209 template <FrameKind KIND> GetPrevFromBoundary(void * ptr)210 static SlotType *GetPrevFromBoundary(void *ptr) 211 { 212 // In current implementation fp must point to previous fp 213 static_assert(BoundaryFrame<KIND>::FP_OFFSET == 0); 214 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 215 return *(reinterpret_cast<SlotType **>(ptr)); 216 } 217 218 template <FrameKind KIND> GetBoundaryFrameMethod(const void * ptr)219 static uintptr_t GetBoundaryFrameMethod(const void *ptr) 220 { 221 auto frameMethod = reinterpret_cast<uintptr_t>(GetMethodFromBoundary<KIND>(ptr)); 222 return frameMethod; 223 } 224 225 template <FrameKind KIND> IsBoundaryFrame(const void * ptr)226 static bool IsBoundaryFrame(const void *ptr) 227 { 228 if constexpr (KIND == FrameKind::INTERPRETER) { // NOLINT 229 return GetBoundaryFrameMethod<KIND>(ptr) == COMPILED_CODE_TO_INTERPRETER; 230 } else { // NOLINT 231 return GetBoundaryFrameMethod<KIND>(ptr) == INTERPRETER_TO_COMPILED_CODE; 232 } 233 } 234 IsInlined()235 bool IsInlined() const 236 { 237 return inlineDepth_ != -1; 238 } 239 240 // Dump modify walker state - you must call it only for rvalue object 241 void Dump(std::ostream &os, bool printVregs = false) &&; 242 243 CalleeRegsBuffer &GetCalleeRegsForDeoptimize(); 244 245 private: 246 // These are shortenings for General-purpose register bank (scalar integer and pointer arithmetic) FirstCalleeIntReg()247 inline constexpr size_t FirstCalleeIntReg() 248 { 249 return GetFirstCalleeReg(RUNTIME_ARCH, false); 250 } LastCalleeIntReg()251 inline constexpr size_t LastCalleeIntReg() 252 { 253 return GetLastCalleeReg(RUNTIME_ARCH, false); 254 } CalleeIntRegsCount()255 inline constexpr size_t CalleeIntRegsCount() 256 { 257 return GetCalleeRegsCount(RUNTIME_ARCH, false); 258 } 259 260 // These are shortenings for SIMD and Floating-Point register bank FirstCalleeFpReg()261 inline constexpr size_t FirstCalleeFpReg() 262 { 263 return GetFirstCalleeReg(RUNTIME_ARCH, true); 264 } LastCalleeFpReg()265 inline constexpr size_t LastCalleeFpReg() 266 { 267 return GetLastCalleeReg(RUNTIME_ARCH, true); 268 } CalleeFpRegsCount()269 inline constexpr size_t CalleeFpRegsCount() 270 { 271 return GetCalleeRegsCount(RUNTIME_ARCH, true); 272 } 273 274 // Struct to keep pointers to stack slots holding callee-saved regs values 275 // and corresponding callee-saved regs masks. 276 struct CalleeStorage { 277 std::array<uintptr_t *, CALLEE_REGS_COUNT> stack = {nullptr}; 278 RegMask intRegsMask {0}; 279 RegMask fpRegsMask {0}; 280 }; 281 282 void InitCalleeBuffer(SlotType *calleeSlots, CalleeStorage *prevCallees); 283 CFrameType CreateCFrame(SlotType *ptr, uintptr_t npc, SlotType *calleeSlots, CalleeStorage *prevCallees = nullptr); 284 285 template <bool CREATE> 286 CFrameType CreateCFrameForC2IBridge(Frame *frame); 287 288 template <bool OBJECTS, bool WITH_REG_INFO, typename Func> 289 bool IterateRegs(Func func); 290 291 template <bool WITH_REG_INFO, typename Func> 292 bool IterateAllRegsForCFrame(Func func); 293 294 template <bool OBJECTS, bool WITH_REG_INFO, typename Func> 295 bool IterateRegsForCFrameStatic(Func func); 296 297 template <bool OBJECTS, bool WITH_REG_INFO, typename Func> 298 bool IterateRegsForCFrameDynamic(Func func); 299 300 template <bool OBJECTS, bool WITH_REG_INFO, typename Func> 301 bool IterateRegsForIFrame(Func func); 302 303 template <bool OBJECTS, bool WITH_REG_INFO, VRegInfo::Type OBJ_TYPE, VRegInfo::Type PRIMITIVE_TYPE, class F, 304 typename Func> 305 bool IterateRegsForIFrameInternal(F frameHandler, Func func); 306 307 FrameVariant GetTopFrameFromFp(void *ptr, bool isFrameCompiled, uintptr_t npc); 308 309 void NextFromCFrame(); 310 void NextFromIFrame(); 311 GetMethodFromCBoundary(void * ptr)312 static Method *GetMethodFromCBoundary(void *ptr) 313 { 314 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 315 return *reinterpret_cast<Method **>(reinterpret_cast<SlotType *>(ptr) - CFrameLayout::MethodSlot::Start()); 316 } 317 318 template <FrameKind KIND> GetMethodFromBoundary(void * ptr)319 static Method *GetMethodFromBoundary(void *ptr) 320 { 321 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 322 return *(reinterpret_cast<Method **>(ptr) + BoundaryFrame<KIND>::METHOD_OFFSET); 323 } 324 325 template <FrameKind KIND> GetMethodFromBoundary(const void * ptr)326 static const Method *GetMethodFromBoundary(const void *ptr) 327 { 328 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 329 return *(reinterpret_cast<Method *const *>(ptr) + BoundaryFrame<KIND>::METHOD_OFFSET); 330 } 331 332 template <FrameKind KIND> GetReturnAddressFromBoundary(const void * ptr)333 static uintptr_t GetReturnAddressFromBoundary(const void *ptr) 334 { 335 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 336 return *(reinterpret_cast<const uintptr_t *>(ptr) + BoundaryFrame<KIND>::RETURN_OFFSET); 337 } 338 339 template <FrameKind KIND> GetCalleeStackFromBoundary(void * ptr)340 static SlotType *GetCalleeStackFromBoundary(void *ptr) 341 { 342 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 343 return reinterpret_cast<SlotType *>(ptr) + BoundaryFrame<KIND>::CALLEES_OFFSET; 344 } 345 GetCFrameBytecodePc()346 uintptr_t GetCFrameBytecodePc() const 347 { 348 if (GetCFrame().IsNative()) { 349 return 0; 350 } 351 if (IsInlined()) { 352 auto ii = codeInfo_.GetInlineInfo(stackmap_, inlineDepth_); 353 return ii.GetBytecodePc(); 354 } 355 return stackmap_.GetBytecodePc(); 356 } GetCFrameNativePc()357 uintptr_t GetCFrameNativePc() const 358 { 359 return GetCFrame().IsNative() ? 0 : stackmap_.GetNativePcUnpacked(); 360 } 361 362 bool HandleAddingAsCFrame(); 363 364 bool HandleAddingAsIFrame(); 365 366 void SetPrevFrame(FrameKind *prevFrameKind, void **prevFrame, CFrameType *cframe); 367 368 Frame *GetFrameFromPrevFrame(Frame *prevFrame); 369 370 void DumpVRegs(std::ostream &os); 371 372 #ifndef NDEBUG 373 void DebugSingleFrameVerify(); 374 #endif 375 376 void DumpVRegLocation(std::ostream &os, VRegInfo ®Info); 377 378 public: 379 class EnvData { 380 using EnvType = coretypes::TaggedType; 381 382 public: 383 template <typename Allocator> 384 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) EnvData(const compiler::CodeInfo::VRegList<Allocator> & vregList,const CFrameType & cframe,const CodeInfo & codeInfo,SlotType ** calleeStack)385 explicit EnvData(const compiler::CodeInfo::VRegList<Allocator> &vregList, const CFrameType &cframe, 386 const CodeInfo &codeInfo, SlotType **calleeStack) 387 { 388 for (size_t i = 0; i < VRegInfo::ENV_COUNT; i++) { 389 auto vreg = vregList[vregList.size() - (VRegInfo::ENV_COUNT - i)]; 390 envData_[i] = static_cast<EnvType>(cframe.GetVRegValue<true>(vreg, codeInfo, calleeStack).GetValue()); 391 } 392 } 393 394 const EnvType &operator[](VRegInfo::VRegType type) const 395 { 396 return envData_[type - VRegInfo::VRegType::ENV_BEGIN]; 397 }; 398 399 private: 400 std::array<EnvType, VRegInfo::ENV_COUNT> envData_; 401 }; 402 403 private: 404 FrameVariant frame_ {nullptr}; 405 UnwindPolicy policy_ {UnwindPolicy::ALL}; 406 CodeInfo codeInfo_; 407 compiler::StackMap stackmap_; 408 int inlineDepth_ {-1}; 409 CalleeStorage calleeStack_; 410 CalleeStorage prevCalleeStack_; 411 CalleeRegsBuffer deoptCalleeRegs_ = {0}; 412 }; 413 414 static_assert((BoundaryFrame<FrameKind::INTERPRETER>::METHOD_OFFSET) * sizeof(uintptr_t) == Frame::GetMethodOffset()); 415 static_assert((BoundaryFrame<FrameKind::INTERPRETER>::FP_OFFSET) * sizeof(uintptr_t) == Frame::GetPrevFrameOffset()); 416 417 } // namespace ark 418 419 #endif // PANDA_RUNTIME_STACK_WALKER_H 420